X-Git-Url: https://oss.titaniummirror.com/gitweb/?a=blobdiff_plain;f=gcc%2Fconfig%2Fcris%2Fcris.c;h=9bae9cdb44ed42e025c6073d0791ea996ac44bf1;hb=6fed43773c9b0ce596dca5686f37ac3fc0fa11c0;hp=b4d433bece923ae3dbe02e72e0f1d0ca4fa81eb2;hpb=27b11d56b743098deb193d510b337ba22dc52e5c;p=msp430-gcc.git diff --git a/gcc/config/cris/cris.c b/gcc/config/cris/cris.c index b4d433be..9bae9cdb 100644 --- a/gcc/config/cris/cris.c +++ b/gcc/config/cris/cris.c @@ -1,12 +1,13 @@ /* Definitions for GCC. Part of the machine description for CRIS. - Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. + Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, + 2008 Free Software Foundation, Inc. Contributed by Axis Communications. Written by Hans-Peter Nilsson. 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 2, or (at your option) +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, @@ -15,12 +16,13 @@ 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 COPYING. If not, write to -the Free Software Foundation, 59 Temple Place - Suite 330, -Boston, MA 02111-1307, USA. */ +along with GCC; see the file COPYING3. If not see +. */ #include "config.h" #include "system.h" +#include "coretypes.h" +#include "tm.h" #include "rtl.h" #include "regs.h" #include "hard-reg-set.h" @@ -35,26 +37,21 @@ Boston, MA 02111-1307, USA. */ #include "function.h" #include "toplev.h" #include "recog.h" +#include "reload.h" #include "tm_p.h" #include "debug.h" #include "output.h" #include "target.h" #include "target-def.h" +#include "ggc.h" +#include "optabs.h" +#include "df.h" /* Usable when we have an amount to add or subtract, and want the optimal size of the insn. */ #define ADDITIVE_SIZE_MODIFIER(size) \ ((size) <= 63 ? "q" : (size) <= 255 ? "u.b" : (size) <= 65535 ? "u.w" : ".d") -#define ASSERT_PLT_UNSPEC(x) \ - do \ - { \ - if (XEXP (x, 1) != NULL_RTX \ - || (GET_CODE (XVECEXP (x, 0, 0)) != SYMBOL_REF \ - && GET_CODE (XVECEXP (x, 0, 0)) != LABEL_REF)) \ - abort (); \ - } while (0) - #define LOSE_AND_RETURN(msgid, x) \ do \ { \ @@ -62,59 +59,68 @@ Boston, MA 02111-1307, USA. */ return; \ } while (0) +enum cris_retinsn_type + { CRIS_RETINSN_UNKNOWN = 0, CRIS_RETINSN_RET, CRIS_RETINSN_JUMP }; + /* Per-function machine data. */ -struct machine_function +struct machine_function GTY(()) { int needs_return_address_on_stack; + + /* This is the number of registers we save in the prologue due to + stdarg. */ + int stdarg_regs; + + enum cris_retinsn_type return_type; }; /* This little fix suppresses the 'u' or 's' when '%e' in assembly pattern. */ static char cris_output_insn_is_bound = 0; -/* This one suppresses printing out the "rPIC+" in - "rPIC+sym:GOTOFF+offset" when doing PIC. For a PLT symbol, it - suppresses outputting it as [rPIC+sym:GOTPLT] and outputs similarly - just the "sym:GOTOFF" part. */ -static int cris_pic_sympart_only = 0; +/* In code for output macros, this is how we know whether e.g. constant + goes in code or in a static initializer. */ +static int in_code = 0; /* Fix for reg_overlap_mentioned_p. */ -static int cris_reg_overlap_mentioned_p PARAMS ((rtx, rtx)); +static int cris_reg_overlap_mentioned_p (rtx, rtx); + +static void cris_print_base (rtx, FILE *); -static void cris_print_base PARAMS ((rtx, FILE *)); +static void cris_print_index (rtx, FILE *); -static void cris_print_index PARAMS ((rtx, FILE *)); +static void cris_output_addr_const (FILE *, rtx); -static void cris_init_machine_status PARAMS ((struct function *)); +static struct machine_function * cris_init_machine_status (void); -static int cris_initial_frame_pointer_offset PARAMS ((void)); +static rtx cris_struct_value_rtx (tree, int); -static int saved_regs_mentioned PARAMS ((rtx)); +static void cris_setup_incoming_varargs (CUMULATIVE_ARGS *, enum machine_mode, + tree type, int *, int); -static void cris_target_asm_function_prologue - PARAMS ((FILE *, HOST_WIDE_INT)); +static int cris_initial_frame_pointer_offset (void); -static void cris_target_asm_function_epilogue - PARAMS ((FILE *, HOST_WIDE_INT)); +static int saved_regs_mentioned (rtx); -static void cris_operand_lossage PARAMS ((const char *, rtx)); +static void cris_operand_lossage (const char *, rtx); -/* The function cris_target_asm_function_epilogue puts the last insn to - output here. It always fits; there won't be a symbol operand. Used in - delay_slots_for_epilogue and function_epilogue. */ -static char save_last[80]; +static int cris_reg_saved_in_regsave_area (unsigned int, bool); -/* This is the argument from the "-max-stack-stackframe=" option. */ -const char *cris_max_stackframe_str; +static void cris_asm_output_mi_thunk + (FILE *, tree, HOST_WIDE_INT, HOST_WIDE_INT, tree); -/* This is the argument from the "-march=" option. */ -const char *cris_cpu_str; +static void cris_file_start (void); +static void cris_init_libfuncs (void); -/* This is the argument from the "-mtune=" option. */ -const char *cris_tune_str; +static bool cris_rtx_costs (rtx, int, int, int *, bool); +static int cris_address_cost (rtx, bool); +static bool cris_pass_by_reference (CUMULATIVE_ARGS *, enum machine_mode, + const_tree, bool); +static int cris_arg_partial_bytes (CUMULATIVE_ARGS *, enum machine_mode, + tree, bool); +static tree cris_md_asm_clobbers (tree, tree, tree); -/* This is the argument from the "-melinux-stacksize=" option. */ -const char *cris_elinux_stacksize_str; +static bool cris_handle_option (size_t, const char *, int); /* This is the parsed result of the "-max-stack-stackframe=" option. If it (still) is zero, then there was no such option given. */ @@ -142,295 +148,277 @@ int cris_cpu_version = CRIS_DEFAULT_CPU_VERSION; #undef TARGET_ASM_UNALIGNED_DI_OP #define TARGET_ASM_UNALIGNED_DI_OP TARGET_ASM_ALIGNED_DI_OP -#undef TARGET_ASM_FUNCTION_PROLOGUE -#define TARGET_ASM_FUNCTION_PROLOGUE cris_target_asm_function_prologue - -#undef TARGET_ASM_FUNCTION_EPILOGUE -#define TARGET_ASM_FUNCTION_EPILOGUE cris_target_asm_function_epilogue +#undef TARGET_ASM_OUTPUT_MI_THUNK +#define TARGET_ASM_OUTPUT_MI_THUNK cris_asm_output_mi_thunk +#undef TARGET_ASM_CAN_OUTPUT_MI_THUNK +#define TARGET_ASM_CAN_OUTPUT_MI_THUNK default_can_output_mi_thunk_no_vcall + +#undef TARGET_ASM_FILE_START +#define TARGET_ASM_FILE_START cris_file_start + +#undef TARGET_INIT_LIBFUNCS +#define TARGET_INIT_LIBFUNCS cris_init_libfuncs + +#undef TARGET_RTX_COSTS +#define TARGET_RTX_COSTS cris_rtx_costs +#undef TARGET_ADDRESS_COST +#define TARGET_ADDRESS_COST cris_address_cost + +#undef TARGET_PROMOTE_FUNCTION_ARGS +#define TARGET_PROMOTE_FUNCTION_ARGS hook_bool_const_tree_true +#undef TARGET_STRUCT_VALUE_RTX +#define TARGET_STRUCT_VALUE_RTX cris_struct_value_rtx +#undef TARGET_SETUP_INCOMING_VARARGS +#define TARGET_SETUP_INCOMING_VARARGS cris_setup_incoming_varargs +#undef TARGET_PASS_BY_REFERENCE +#define TARGET_PASS_BY_REFERENCE cris_pass_by_reference +#undef TARGET_ARG_PARTIAL_BYTES +#define TARGET_ARG_PARTIAL_BYTES cris_arg_partial_bytes +#undef TARGET_MD_ASM_CLOBBERS +#define TARGET_MD_ASM_CLOBBERS cris_md_asm_clobbers +#undef TARGET_DEFAULT_TARGET_FLAGS +#define TARGET_DEFAULT_TARGET_FLAGS (TARGET_DEFAULT | CRIS_SUBTARGET_DEFAULT) +#undef TARGET_HANDLE_OPTION +#define TARGET_HANDLE_OPTION cris_handle_option struct gcc_target targetm = TARGET_INITIALIZER; -/* Predicate functions. */ - -/* This checks a part of an address, the one that is not a plain register - for an addressing mode using BDAP. - Allowed operands is either: - a) a register - b) a CONST operand (but not a symbol when generating PIC) - c) a [r] or [r+] in SImode, or sign-extend from HI or QI. */ +/* Helper for cris_load_multiple_op and cris_ret_movem_op. */ -int -cris_bdap_operand (op, mode) - rtx op; - enum machine_mode mode; +bool +cris_movem_load_rest_p (rtx op, int offs) { - register enum rtx_code code = GET_CODE (op); - - if (mode != SImode && (mode != VOIDmode || GET_MODE (op) != VOIDmode)) - return 0; - - /* Just return whether this is a simple register or constant. */ - if (register_operand (op, mode) - || (CONSTANT_P (op) && !(flag_pic && cris_symbol (op)))) - return 1; - - /* Is it a [r] or possibly a [r+]? */ - if (code == MEM) + unsigned int reg_count = XVECLEN (op, 0) - offs; + rtx src_addr; + int i; + rtx elt; + int setno; + int regno_dir = 1; + unsigned int regno = 0; + + /* Perform a quick check so we don't blow up below. FIXME: Adjust for + other than (MEM reg). */ + if (reg_count <= 1 + || GET_CODE (XVECEXP (op, 0, offs)) != SET + || !REG_P (SET_DEST (XVECEXP (op, 0, offs))) + || !MEM_P (SET_SRC (XVECEXP (op, 0, offs)))) + return false; + + /* Check a possible post-inc indicator. */ + if (GET_CODE (SET_SRC (XVECEXP (op, 0, offs + 1))) == PLUS) { - rtx tem = XEXP (op, 0); - - if (mode == SImode - && (register_operand (tem, SImode) - || (GET_CODE (tem) == POST_INC - && register_operand (XEXP (tem, 0), SImode)))) - return 1; - else - return 0; + rtx reg = XEXP (SET_SRC (XVECEXP (op, 0, offs + 1)), 0); + rtx inc = XEXP (SET_SRC (XVECEXP (op, 0, offs + 1)), 1); + + reg_count--; + + if (reg_count == 1 + || !REG_P (reg) + || !REG_P (SET_DEST (XVECEXP (op, 0, offs + 1))) + || REGNO (reg) != REGNO (SET_DEST (XVECEXP (op, 0, offs + 1))) + || !CONST_INT_P (inc) + || INTVAL (inc) != (HOST_WIDE_INT) reg_count * 4) + return false; + i = offs + 2; } + else + i = offs + 1; - /* Perhaps a sign-extended mem: [r].(b|w) or [r+].(b|w)? */ - if (code == SIGN_EXTEND) + if (!TARGET_V32) { - rtx tem = XEXP (op, 0); - - if (GET_CODE (tem) != MEM) - return 0; - - tem = XEXP (tem, 0); - if (mode == SImode - && (register_operand (tem, SImode) - || (GET_CODE (tem) == POST_INC - && register_operand (XEXP (tem, 0), SImode)))) - return 1; - else - return 0; + regno_dir = -1; + regno = reg_count - 1; } - return 0; -} - -/* This is similar to cris_bdap_operand: - It checks a part of an address, the one that is not a plain register - for an addressing mode using BDAP *or* BIAP. - Allowed operands is either: - a) a register - b) a CONST operand (but not a symbol when generating PIC) - c) a mult of (1, 2 or 4) and a register - d) a [r] or [r+] in SImode, or sign-extend from HI or QI. */ - -int -cris_bdap_biap_operand (op, mode) - rtx op; - enum machine_mode mode; -{ - register enum rtx_code code = GET_CODE (op); - rtx reg; - rtx val; - - /* Check for bdap operand. */ - if (cris_bdap_operand (op, mode)) - return 1; - - if (mode != SImode && (mode != VOIDmode || GET_MODE (op) != VOIDmode)) - return 0; + elt = XVECEXP (op, 0, offs); + src_addr = XEXP (SET_SRC (elt), 0); - /* Check that we're looking at a BIAP operand. */ - if (code != MULT) - return 0; + if (GET_CODE (elt) != SET + || !REG_P (SET_DEST (elt)) + || GET_MODE (SET_DEST (elt)) != SImode + || REGNO (SET_DEST (elt)) != regno + || !MEM_P (SET_SRC (elt)) + || GET_MODE (SET_SRC (elt)) != SImode + || !memory_address_p (SImode, src_addr)) + return false; - /* Canonicalize register and multiplicand. */ - if (GET_CODE (XEXP (op, 0)) == CONST_INT) + for (setno = 1; i < XVECLEN (op, 0); setno++, i++) { - val = XEXP (op, 0); - reg = XEXP (op, 1); + rtx elt = XVECEXP (op, 0, i); + regno += regno_dir; + + if (GET_CODE (elt) != SET + || !REG_P (SET_DEST (elt)) + || GET_MODE (SET_DEST (elt)) != SImode + || REGNO (SET_DEST (elt)) != regno + || !MEM_P (SET_SRC (elt)) + || GET_MODE (SET_SRC (elt)) != SImode + || GET_CODE (XEXP (SET_SRC (elt), 0)) != PLUS + || ! rtx_equal_p (XEXP (XEXP (SET_SRC (elt), 0), 0), src_addr) + || !CONST_INT_P (XEXP (XEXP (SET_SRC (elt), 0), 1)) + || INTVAL (XEXP (XEXP (SET_SRC (elt), 0), 1)) != setno * 4) + return false; } - else - { - val = XEXP (op, 1); - reg = XEXP (op, 0); - } - - /* Check that the operands are correct after canonicalization. */ - if (! register_operand (reg, SImode) || GET_CODE (val) != CONST_INT) - return 0; - - /* Check that the multiplicand has a valid value. */ - if ((code == MULT - && (INTVAL (val) == 1 || INTVAL (val) == 2 || INTVAL (val) == 4))) - return 1; - - return 0; -} - -/* Check if MODE is same as mode for X, and X is PLUS, MINUS, IOR or - AND or UMIN. */ - -int -cris_orthogonal_operator (x, mode) - rtx x; - enum machine_mode mode; -{ - enum rtx_code code = GET_CODE (x); - - if (mode == VOIDmode) - mode = GET_MODE (x); - - return (GET_MODE (x) == mode - && (code == PLUS || code == MINUS - || code == IOR || code == AND || code == UMIN)); -} - -/* Check if MODE is same as mode for X, and X is PLUS, IOR or AND or - UMIN. */ - -int -cris_commutative_orth_op (x, mode) - rtx x; - enum machine_mode mode; -{ - enum rtx_code code = GET_CODE (x); - - if (mode == VOIDmode) - mode = GET_MODE (x); - - return (GET_MODE (x) == mode && - (code == PLUS - || code == IOR || code == AND || code == UMIN)); -} - -/* Check if MODE is same as mode for X, and X is PLUS or MINUS or UMIN. */ - -int -cris_operand_extend_operator (x, mode) - rtx x; - enum machine_mode mode; -{ - enum rtx_code code = GET_CODE (x); - - if (mode == VOIDmode) - mode = GET_MODE (x); - return (GET_MODE (x) == mode - && (code == PLUS || code == MINUS || code == UMIN)); + return true; } -/* Check to see if MODE is same as mode for X, and X is SIGN_EXTEND or - ZERO_EXTEND. */ +/* Worker function for predicate for the parallel contents in a movem + to-memory. */ -int -cris_extend_operator (x, mode) - rtx x; - enum machine_mode mode; -{ - enum rtx_code code = GET_CODE (x); - - if (mode == VOIDmode) - mode = GET_MODE (x); - - return - (GET_MODE (x) == mode && (code == SIGN_EXTEND || code == ZERO_EXTEND)); -} - -/* Check to see if MODE is same as mode for X, and X is PLUS or BOUND. */ - -int -cris_plus_or_bound_operator (x, mode) - rtx x; - enum machine_mode mode; +bool +cris_store_multiple_op_p (rtx op) { - enum rtx_code code = GET_CODE (x); - - if (mode == VOIDmode) - mode = GET_MODE (x); - - return - (GET_MODE (x) == mode && (code == UMIN || code == PLUS)); -} + int reg_count = XVECLEN (op, 0); + rtx dest; + rtx dest_addr; + rtx dest_base; + int i; + rtx elt; + int setno; + int regno_dir = 1; + int regno = 0; + int offset = 0; -/* Since with -fPIC, not all symbols are valid PIC symbols or indeed - general_operands, we have to have a predicate that matches it for the - "movsi" expander. */ + /* Perform a quick check so we don't blow up below. FIXME: Adjust for + other than (MEM reg) and (MEM (PLUS reg const)). */ + if (reg_count <= 1) + return false; -int -cris_general_operand_or_symbol (op, mode) - rtx op; - enum machine_mode mode; -{ - return general_operand (op, mode) - || (CONSTANT_P (op) && cris_symbol (op)); -} + elt = XVECEXP (op, 0, 0); -/* Since a PIC symbol without a GOT entry is not a general_operand, we - have to have a predicate that matches it. We use this in the expanded - "movsi" anonymous pattern for PIC symbols. */ + if (GET_CODE (elt) != SET) + return false; -int -cris_general_operand_or_gotless_symbol (op, mode) - rtx op; - enum machine_mode mode; -{ - return general_operand (op, mode) - || (CONSTANT_P (op) && cris_gotless_symbol (op)); -} + dest = SET_DEST (elt); -/* Since a PLT symbol is not a general_operand, we have to have a - predicate that matches it when we need it. We use this in the expanded - "call" and "call_value" anonymous patterns. */ + if (!REG_P (SET_SRC (elt)) || !MEM_P (dest)) + return false; -int -cris_general_operand_or_plt_symbol (op, mode) - rtx op; - enum machine_mode mode; -{ - return general_operand (op, mode) - || (GET_CODE (op) == CONST - && GET_CODE (XEXP (op, 0)) == UNSPEC - && !TARGET_AVOID_GOTPLT); -} + dest_addr = XEXP (dest, 0); -/* This matches a (MEM (general_operand)) or - (MEM (cris_general_operand_or_symbol)). The second one isn't a valid - memory_operand, so we need this predicate to recognize call - destinations before we change them to a PLT operand (by wrapping in - UNSPEC 0). */ + /* Check a possible post-inc indicator. */ + if (GET_CODE (SET_SRC (XVECEXP (op, 0, 1))) == PLUS) + { + rtx reg = XEXP (SET_SRC (XVECEXP (op, 0, 1)), 0); + rtx inc = XEXP (SET_SRC (XVECEXP (op, 0, 1)), 1); + + reg_count--; + + if (reg_count == 1 + || !REG_P (reg) + || !REG_P (SET_DEST (XVECEXP (op, 0, 1))) + || REGNO (reg) != REGNO (SET_DEST (XVECEXP (op, 0, 1))) + || !CONST_INT_P (inc) + /* Support increment by number of registers, and by the offset + of the destination, if it has the form (MEM (PLUS reg + offset)). */ + || !((REG_P (dest_addr) + && REGNO (dest_addr) == REGNO (reg) + && INTVAL (inc) == (HOST_WIDE_INT) reg_count * 4) + || (GET_CODE (dest_addr) == PLUS + && REG_P (XEXP (dest_addr, 0)) + && REGNO (XEXP (dest_addr, 0)) == REGNO (reg) + && CONST_INT_P (XEXP (dest_addr, 1)) + && INTVAL (XEXP (dest_addr, 1)) == INTVAL (inc)))) + return false; + + i = 2; + } + else + i = 1; -int -cris_mem_call_operand (op, mode) - rtx op; - enum machine_mode mode; -{ - rtx xmem; + if (!TARGET_V32) + { + regno_dir = -1; + regno = reg_count - 1; + } - if (GET_CODE (op) != MEM) - return 0; + if (GET_CODE (elt) != SET + || !REG_P (SET_SRC (elt)) + || GET_MODE (SET_SRC (elt)) != SImode + || REGNO (SET_SRC (elt)) != (unsigned int) regno + || !MEM_P (SET_DEST (elt)) + || GET_MODE (SET_DEST (elt)) != SImode) + return false; - if (memory_operand (op, mode)) - return 1; + if (REG_P (dest_addr)) + { + dest_base = dest_addr; + offset = 0; + } + else if (GET_CODE (dest_addr) == PLUS + && REG_P (XEXP (dest_addr, 0)) + && CONST_INT_P (XEXP (dest_addr, 1))) + { + dest_base = XEXP (dest_addr, 0); + offset = INTVAL (XEXP (dest_addr, 1)); + } + else + return false; - xmem = XEXP (op, 0); + for (setno = 1; i < XVECLEN (op, 0); setno++, i++) + { + rtx elt = XVECEXP (op, 0, i); + regno += regno_dir; + + if (GET_CODE (elt) != SET + || !REG_P (SET_SRC (elt)) + || GET_MODE (SET_SRC (elt)) != SImode + || REGNO (SET_SRC (elt)) != (unsigned int) regno + || !MEM_P (SET_DEST (elt)) + || GET_MODE (SET_DEST (elt)) != SImode + || GET_CODE (XEXP (SET_DEST (elt), 0)) != PLUS + || ! rtx_equal_p (XEXP (XEXP (SET_DEST (elt), 0), 0), dest_base) + || !CONST_INT_P (XEXP (XEXP (SET_DEST (elt), 0), 1)) + || INTVAL (XEXP (XEXP (SET_DEST (elt), 0), 1)) != setno * 4 + offset) + return false; + } - return cris_general_operand_or_symbol (xmem, GET_MODE (op)); + return true; } -/* The CONDITIONAL_REGISTER_USAGE worker. */ +/* The CONDITIONAL_REGISTER_USAGE worker. */ void -cris_conditional_register_usage () +cris_conditional_register_usage (void) { /* FIXME: This isn't nice. We should be able to use that register for something else if the PIC table isn't needed. */ if (flag_pic) fixed_regs[PIC_OFFSET_TABLE_REGNUM] = call_used_regs[PIC_OFFSET_TABLE_REGNUM] = 1; + + /* Allow use of ACR (PC in pre-V32) and tweak order. */ + if (TARGET_V32) + { + static const int reg_alloc_order_v32[] = REG_ALLOC_ORDER_V32; + unsigned int i; + + fixed_regs[CRIS_ACR_REGNUM] = 0; + + for (i = 0; + i < sizeof (reg_alloc_order_v32)/sizeof (reg_alloc_order_v32[0]); + i++) + reg_alloc_order[i] = reg_alloc_order_v32[i]; + } + + if (TARGET_HAS_MUL_INSNS) + fixed_regs[CRIS_MOF_REGNUM] = 0; + + /* On early versions, we must use the 16-bit condition-code register, + which has another name. */ + if (cris_cpu_version < 8) + reg_names[CRIS_CC0_REGNUM] = "ccr"; } -/* Return current_function_uses_pic_offset_table. For use in cris.md, +/* Return crtl->uses_pic_offset_table. For use in cris.md, since some generated files do not include function.h. */ int -cris_cfun_uses_pic_table () +cris_cfun_uses_pic_table (void) { - return current_function_uses_pic_offset_table; + return crtl->uses_pic_offset_table; } /* Given an rtx, return the text string corresponding to the CODE of X. @@ -438,8 +426,7 @@ cris_cfun_uses_pic_table () define_insn. */ const char * -cris_op_str (x) - rtx x; +cris_op_str (rtx x) { cris_output_insn_is_bound = 0; switch (GET_CODE (x)) @@ -453,7 +440,11 @@ cris_op_str (x) break; case MULT: - return "mul"; + /* This function is for retrieving a part of an instruction name for + an operator, for immediate output. If that ever happens for + MULT, we need to apply TARGET_MUL_BUG in the caller. Make sure + we notice. */ + internal_error ("MULT case in cris_op_str"); break; case DIV: @@ -489,7 +480,7 @@ cris_op_str (x) break; case UMIN: - /* Used to control the sign/zero-extend character for the 'e' modifier. + /* Used to control the sign/zero-extend character for the 'E' modifier. BOUND has none. */ cris_output_insn_is_bound = 1; return "bound"; @@ -507,9 +498,7 @@ cris_op_str (x) categorization of the error. */ static void -cris_operand_lossage (msgid, op) - const char *msgid; - rtx op; +cris_operand_lossage (const char *msgid, rtx op) { debug_rtx (op); output_operand_lossage ("%s", msgid); @@ -518,15 +507,11 @@ cris_operand_lossage (msgid, op) /* Print an index part of an address to file. */ static void -cris_print_index (index, file) - rtx index; - FILE * file; +cris_print_index (rtx index, FILE *file) { - rtx inner = XEXP (index, 0); - /* Make the index "additive" unless we'll output a negative number, in which case the sign character is free (as in free beer). */ - if (GET_CODE (index) != CONST_INT || INTVAL (index) >= 0) + if (!CONST_INT_P (index) || INTVAL (index) >= 0) putc ('+', file); if (REG_P (index)) @@ -540,9 +525,9 @@ cris_print_index (index, file) putc (INTVAL (XEXP (index, 1)) == 2 ? 'w' : 'd', file); } - else if (GET_CODE (index) == SIGN_EXTEND && - GET_CODE (inner) == MEM) + else if (GET_CODE (index) == SIGN_EXTEND && MEM_P (XEXP (index, 0))) { + rtx inner = XEXP (index, 0); rtx inner_inner = XEXP (inner, 0); if (GET_CODE (inner_inner) == POST_INC) @@ -558,8 +543,9 @@ cris_print_index (index, file) putc (GET_MODE (inner) == HImode ? 'w' : 'b', file); } } - else if (GET_CODE (index) == MEM) + else if (MEM_P (index)) { + rtx inner = XEXP (index, 0); if (GET_CODE (inner) == POST_INC) fprintf (file, "[$%s+].d", reg_names[REGNO (XEXP (inner, 0))]); else @@ -573,14 +559,15 @@ cris_print_index (index, file) /* Print a base rtx of an address to file. */ static void -cris_print_base (base, file) - rtx base; - FILE *file; +cris_print_base (rtx base, FILE *file) { if (REG_P (base)) fprintf (file, "$%s", reg_names[REGNO (base)]); else if (GET_CODE (base) == POST_INC) - fprintf (file, "$%s+", reg_names[REGNO (XEXP (base, 0))]); + { + gcc_assert (REGNO (XEXP (base, 0)) != CRIS_ACR_REGNUM); + fprintf (file, "$%s+", reg_names[REGNO (XEXP (base, 0))]); + } else cris_operand_lossage ("unexpected base-type in cris_print_base", base); @@ -589,8 +576,7 @@ cris_print_base (base, file) /* Usable as a guard in expressions. */ int -cris_fatal (arg) - char *arg; +cris_fatal (char *arg) { internal_error (arg); @@ -598,284 +584,31 @@ cris_fatal (arg) return 0; } -/* Textual function prologue. */ +/* Return nonzero if REGNO is an ordinary register that *needs* to be + saved together with other registers, possibly by a MOVEM instruction, + or is saved for target-independent reasons. There may be + target-dependent reasons to save the register anyway; this is just a + wrapper for a complicated conditional. */ -static void -cris_target_asm_function_prologue (file, size) - FILE *file; - HOST_WIDE_INT size; +static int +cris_reg_saved_in_regsave_area (unsigned int regno, bool got_really_used) { - int regno; - - /* Shorten the used name for readability. */ - int cfoa_size = current_function_outgoing_args_size; - int last_movem_reg = -1; - int doing_dwarf = dwarf2out_do_frame (); - int framesize; - int faked_args_size = 0; - int cfa_write_offset = 0; - char *cfa_label = NULL; - int return_address_on_stack - = regs_ever_live[CRIS_SRP_REGNUM] - || cfun->machine->needs_return_address_on_stack != 0; - - /* Don't do anything if no prologues or epilogues are wanted. */ - if (!TARGET_PROLOGUE_EPILOGUE) - return; - - if (size < 0) - abort (); - - /* Align the size to what's best for the CPU model. */ - if (TARGET_STACK_ALIGN) - size = TARGET_ALIGN_BY_32 ? (size + 3) & ~3 : (size + 1) & ~1; - - if (current_function_pretend_args_size) - { - int pretend = current_function_pretend_args_size; - for (regno = CRIS_FIRST_ARG_REG + CRIS_MAX_ARGS_IN_REGS - 1; - pretend > 0; - regno--, pretend -= 4) - { - fprintf (file, "\tpush $%s\n", reg_names[regno]); - faked_args_size += 4; - } - } - - framesize = faked_args_size; - - if (doing_dwarf) - { - /* FIXME: Slightly redundant calculation, as we do the same in - pieces below. This offset must be the total adjustment of the - stack-pointer. We can then def_cfa call at the end of this - function with the current implementation of execute_cfa_insn, but - that wouldn't really be clean. */ - - int cfa_offset - = faked_args_size - + (return_address_on_stack ? 4 : 0) - + (frame_pointer_needed ? 4 : 0); - - int cfa_reg; - - if (frame_pointer_needed) - cfa_reg = FRAME_POINTER_REGNUM; - else - { - cfa_reg = STACK_POINTER_REGNUM; - cfa_offset += cris_initial_frame_pointer_offset (); - } - - cfa_label = dwarf2out_cfi_label (); - dwarf2out_def_cfa (cfa_label, cfa_reg, cfa_offset); - - cfa_write_offset = - faked_args_size - 4; - } - - /* Save SRP if not a leaf function. */ - if (return_address_on_stack) - { - fprintf (file, "\tPush $srp\n"); - framesize += 4; - - if (doing_dwarf) - { - dwarf2out_return_save (cfa_label, cfa_write_offset); - cfa_write_offset -= 4; - } - } - - /* Set up frame pointer if needed. */ - if (frame_pointer_needed) - { - fprintf (file, "\tpush $%s\n\tmove.d $sp,$%s\n", - reg_names[FRAME_POINTER_REGNUM], - reg_names[FRAME_POINTER_REGNUM]); - framesize += 4; - - if (doing_dwarf) - { - dwarf2out_reg_save (cfa_label, FRAME_POINTER_REGNUM, - cfa_write_offset); - cfa_write_offset -= 4; - } - } - - /* Local vars are located above saved regs. */ - cfa_write_offset -= size; - - /* Get a contiguous sequence of registers, starting with r0, that need - to be saved. */ - for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) - { - if ((((regs_ever_live[regno] - && !call_used_regs[regno]) - || (regno == (int) PIC_OFFSET_TABLE_REGNUM - && (current_function_uses_pic_offset_table - /* It is saved anyway, if there would be a gap. */ - || (flag_pic - && regs_ever_live[regno + 1] - && !call_used_regs[regno + 1])))) - && (regno != FRAME_POINTER_REGNUM || !frame_pointer_needed) - && regno != CRIS_SRP_REGNUM) - || (current_function_calls_eh_return - && (regno == EH_RETURN_DATA_REGNO (0) - || regno == EH_RETURN_DATA_REGNO (1) - || regno == EH_RETURN_DATA_REGNO (2) - || regno == EH_RETURN_DATA_REGNO (3)))) - { - /* Check if movem may be used for registers so far. */ - if (regno == last_movem_reg + 1) - /* Yes, update next expected register. */ - last_movem_reg++; - else - { - /* We cannot use movem for all registers. We have to flush - any movem:ed registers we got so far. */ - if (last_movem_reg != -1) - { - /* It is a win to use a side-effect assignment for - 64 <= size <= 128. But side-effect on movem was - not usable for CRIS v0..3. Also only do it if - side-effects insns are allowed. */ - if ((last_movem_reg + 1) * 4 + size >= 64 - && (last_movem_reg + 1) * 4 + size <= 128 - && cris_cpu_version >= CRIS_CPU_SVINTO - && TARGET_SIDE_EFFECT_PREFIXES) - fprintf (file, "\tmovem $%s,[$sp=$sp-%d]\n", - reg_names[last_movem_reg], - (last_movem_reg + 1) * 4 + size); - else - { - /* Avoid printing multiple subsequent sub:s for sp. */ - fprintf (file, "\tsub%s %d,$sp\n", - ADDITIVE_SIZE_MODIFIER ((last_movem_reg + 1) - * 4 + size), - (last_movem_reg + 1) * 4 + size); - - fprintf (file, "\tmovem $%s,[$sp]\n", - reg_names[last_movem_reg]); - } - - framesize += (last_movem_reg + 1) * 4 + size; - - if (TARGET_PDEBUG) - fprintf (file, "; frame %d, #regs %d, bytes %d args %d\n", - size, - last_movem_reg + 1, - (last_movem_reg + 1) * 4, - current_function_args_size); - - last_movem_reg = -1; - size = 0; - } - else if (size > 0) - { - /* Local vars on stack, but there are no movem:s. - Just allocate space. */ - fprintf (file, "\tSub%s %d,$sp\n", - ADDITIVE_SIZE_MODIFIER (size), - size); - framesize += size; - size = 0; - } - - fprintf (file, "\tPush $%s\n", reg_names[regno]); - framesize += 4; - } - - if (doing_dwarf) - { - /* Registers are stored lowest numbered at highest address, - which matches the loop order; we just need to update the - write-offset. */ - dwarf2out_reg_save (cfa_label, regno, cfa_write_offset); - cfa_write_offset -= 4; - } - } - } - - /* Check after, if we can movem all registers. This is the normal - case. */ - if (last_movem_reg != -1) - { - /* Side-effect assignment on movem was not supported for CRIS v0..3, - and don't do it if we're asked not to. - - The movem is already accounted for, for unwind. */ - - if ((last_movem_reg + 1) * 4 + size >= 64 - && (last_movem_reg + 1) * 4 + size <= 128 - && cris_cpu_version >= CRIS_CPU_SVINTO - && TARGET_SIDE_EFFECT_PREFIXES) - fprintf (file, "\tmovem $%s,[$sp=$sp-%d]\n", - reg_names[last_movem_reg], - (last_movem_reg+1) * 4 + size); - else - { - /* Avoid printing multiple subsequent sub:s for sp. FIXME: - Clean up the conditional expression. */ - fprintf (file, "\tsub%s %d,$sp\n", - ADDITIVE_SIZE_MODIFIER ((last_movem_reg + 1) * 4 + size), - (last_movem_reg + 1) * 4 + size); - /* To be compatible with v0..v3 means we do not use an assignment - addressing mode with movem. We normally don't need that - anyway. It would only be slightly more efficient for 64..128 - bytes frame size. */ - fprintf (file, "\tmovem $%s,[$sp]\n", reg_names[last_movem_reg]); - } - - framesize += (last_movem_reg + 1) * 4 + size; - - if (TARGET_PDEBUG) - fprintf (file, "; frame %d, #regs %d, bytes %d args %d\n", - size, - last_movem_reg + 1, - (last_movem_reg + 1) * 4, - current_function_args_size); - - /* We have to put outgoing argument space after regs. */ - if (cfoa_size) - { - /* This does not need to be accounted for, for unwind. */ - - fprintf (file, "\tSub%s %d,$sp\n", - ADDITIVE_SIZE_MODIFIER (cfoa_size), - cfoa_size); - framesize += cfoa_size; - } - } - else if ((size + cfoa_size) > 0) - { - /* This does not need to be accounted for, for unwind. */ - - /* Local vars on stack, and we could not use movem. Add a sub here. */ - fprintf (file, "\tSub%s %d,$sp\n", - ADDITIVE_SIZE_MODIFIER (size + cfoa_size), - cfoa_size + size); - framesize += size + cfoa_size; - } - - /* Set up the PIC register. */ - if (current_function_uses_pic_offset_table) - asm_fprintf (file, "\tmove.d $pc,$%s\n\tsub.d .:GOTOFF,$%s\n", - reg_names[PIC_OFFSET_TABLE_REGNUM], - reg_names[PIC_OFFSET_TABLE_REGNUM]); - - if (TARGET_PDEBUG) - fprintf (file, - "; parm #%d @ %d; frame %d, FP-SP is %d; leaf: %s%s; fp %s, outg: %d arg %d\n", - CRIS_MAX_ARGS_IN_REGS + 1, FIRST_PARM_OFFSET (0), - get_frame_size (), - cris_initial_frame_pointer_offset (), - leaf_function_p () ? "yes" : "no", - return_address_on_stack ? "no" :"yes", - frame_pointer_needed ? "yes" : "no", - cfoa_size, current_function_args_size); - - if (cris_max_stackframe && framesize > cris_max_stackframe) - warning ("stackframe too big: %d bytes", framesize); + return + (((df_regs_ever_live_p (regno) + && !call_used_regs[regno]) + || (regno == PIC_OFFSET_TABLE_REGNUM + && (got_really_used + /* It is saved anyway, if there would be a gap. */ + || (flag_pic + && df_regs_ever_live_p (regno + 1) + && !call_used_regs[regno + 1])))) + && (regno != FRAME_POINTER_REGNUM || !frame_pointer_needed) + && regno != CRIS_SRP_REGNUM) + || (crtl->calls_eh_return + && (regno == EH_RETURN_DATA_REGNO (0) + || regno == EH_RETURN_DATA_REGNO (1) + || regno == EH_RETURN_DATA_REGNO (2) + || regno == EH_RETURN_DATA_REGNO (3))); } /* Return nonzero if there are regs mentioned in the insn that are not all @@ -883,8 +616,7 @@ cris_target_asm_function_prologue (file, size) can be put in the epilogue. */ static int -saved_regs_mentioned (x) - rtx x; +saved_regs_mentioned (rtx x) { int i; const char *fmt; @@ -930,384 +662,98 @@ saved_regs_mentioned (x) return 0; } -/* Figure out if the insn may be put in the epilogue. */ +/* The PRINT_OPERAND worker. */ -int -cris_eligible_for_epilogue_delay (insn) - rtx insn; +void +cris_print_operand (FILE *file, rtx x, int code) { - /* First of all, it must be as slottable as for a delayed branch insn. */ - if (get_attr_slottable (insn) != SLOTTABLE_YES) - return 0; + rtx operand = x; - /* It must not refer to the stack pointer (may be valid for some cases - that I can't think of). */ - if (reg_mentioned_p (stack_pointer_rtx, PATTERN (insn))) - return 0; + /* Size-strings corresponding to MULT expressions. */ + static const char *const mults[] = { "BAD:0", ".b", ".w", "BAD:3", ".d" }; - /* The frame pointer will be restored in the epilogue, before the - "ret", so it can't be referred to. */ - if (frame_pointer_needed - && reg_mentioned_p (frame_pointer_rtx, PATTERN (insn))) - return 0; + /* New code entries should just be added to the switch below. If + handling is finished, just return. If handling was just a + modification of the operand, the modified operand should be put in + "operand", and then do a break to let default handling + (zero-modifier) output the operand. */ - /* All saved regs are restored before the delayed insn. - This means that we cannot have any instructions that mention the - registers that are restored by the epilogue. */ - if (saved_regs_mentioned (PATTERN (insn))) - return 0; + switch (code) + { + case 'b': + /* Print the unsigned supplied integer as if it were signed + and < 0, i.e print 255 or 65535 as -1, 254, 65534 as -2, etc. */ + if (!CONST_INT_P (x) + || !CRIS_CONST_OK_FOR_LETTER_P (INTVAL (x), 'O')) + LOSE_AND_RETURN ("invalid operand for 'b' modifier", x); + fprintf (file, HOST_WIDE_INT_PRINT_DEC, + INTVAL (x)| (INTVAL (x) <= 255 ? ~255 : ~65535)); + return; - /* It seems to be ok. */ - return 1; -} - -/* Return the number of delay-slots in the epilogue: return 1 if it - contains "ret", else 0. */ - -int -cris_delay_slots_for_epilogue () -{ - /* Check if we use a return insn, which we only do for leaf functions. - Else there is no slot to fill. */ - if (regs_ever_live[CRIS_SRP_REGNUM] - || cfun->machine->needs_return_address_on_stack != 0) - return 0; - - /* By calling function_epilogue with the same parameters as from gcc - we can get info about if the epilogue can fill the delay-slot by itself. - If it is filled from the epilogue, then the corresponding string - is in save_last. - This depends on that the "size" argument to function_epilogue - always is get_frame_size. - FIXME: Kludgy. At least make it a separate function that is not - misnamed or abuses the stream parameter. */ - cris_target_asm_function_epilogue (NULL, get_frame_size ()); - - if (*save_last) - return 1; - return 0; -} - -/* Textual function epilogue. When file is NULL, it serves doubly as - a test for whether the epilogue can fill any "ret" delay-slots by - itself by storing the delay insn in save_last. */ - -static void -cris_target_asm_function_epilogue (file, size) - FILE *file; - HOST_WIDE_INT size; -{ - int regno; - int last_movem_reg = -1; - rtx insn = get_last_insn (); - int argspace_offset = current_function_outgoing_args_size; - int pretend = current_function_pretend_args_size; - int return_address_on_stack - = regs_ever_live[CRIS_SRP_REGNUM] - || cfun->machine->needs_return_address_on_stack != 0; - - save_last[0] = 0; - - if (file && !TARGET_PROLOGUE_EPILOGUE) - return; - - if (TARGET_PDEBUG && file) - fprintf (file, ";;\n"); - - /* Align byte count of stack frame. */ - if (TARGET_STACK_ALIGN) - size = TARGET_ALIGN_BY_32 ? (size + 3) & ~3 : (size + 1) & ~1; - - /* If the last insn was a BARRIER, we don't have to write any code, - then all returns were covered by "return" insns. */ - if (GET_CODE (insn) == NOTE) - insn = prev_nonnote_insn (insn); - if (insn - && (GET_CODE (insn) == BARRIER - /* We must make sure that the insn really is a "return" and - not a conditional branch. Try to match the return exactly, - and if it doesn't match, assume it is a conditional branch - (and output an epilogue). */ - || (GET_CODE (insn) == JUMP_INSN - && GET_CODE (PATTERN (insn)) == RETURN))) - { - if (TARGET_PDEBUG && file) - fprintf (file, ";;;;;\n"); + case 'x': + /* Print assembler code for operator. */ + fprintf (file, "%s", cris_op_str (operand)); return; - } - - /* Check how many saved regs we can movem. They start at r0 and must - be contiguous. */ - for (regno = 0; - regno < FIRST_PSEUDO_REGISTER; - regno++) - if ((((regs_ever_live[regno] - && !call_used_regs[regno]) - || (regno == (int) PIC_OFFSET_TABLE_REGNUM - && (current_function_uses_pic_offset_table - /* It is saved anyway, if there would be a gap. */ - || (flag_pic - && regs_ever_live[regno + 1] - && !call_used_regs[regno + 1])))) - && (regno != FRAME_POINTER_REGNUM || !frame_pointer_needed) - && regno != CRIS_SRP_REGNUM) - || (current_function_calls_eh_return - && (regno == EH_RETURN_DATA_REGNO (0) - || regno == EH_RETURN_DATA_REGNO (1) - || regno == EH_RETURN_DATA_REGNO (2) - || regno == EH_RETURN_DATA_REGNO (3)))) + case 'o': { - if (regno == last_movem_reg + 1) - last_movem_reg++; - else - break; - } + /* A movem modifier working on a parallel; output the register + name. */ + int regno; - for (regno = FIRST_PSEUDO_REGISTER - 1; - regno > last_movem_reg; - regno--) - if ((((regs_ever_live[regno] - && !call_used_regs[regno]) - || (regno == (int) PIC_OFFSET_TABLE_REGNUM - && (current_function_uses_pic_offset_table - /* It is saved anyway, if there would be a gap. */ - || (flag_pic - && regs_ever_live[regno + 1] - && !call_used_regs[regno + 1])))) - && (regno != FRAME_POINTER_REGNUM || !frame_pointer_needed) - && regno != CRIS_SRP_REGNUM) - || (current_function_calls_eh_return - && (regno == EH_RETURN_DATA_REGNO (0) - || regno == EH_RETURN_DATA_REGNO (1) - || regno == EH_RETURN_DATA_REGNO (2) - || regno == EH_RETURN_DATA_REGNO (3)))) - { - if (argspace_offset) - { - /* There is an area for outgoing parameters located before - the saved registers. We have to adjust for that. */ - if (file) - fprintf (file, "\tAdd%s %d,$sp\n", - ADDITIVE_SIZE_MODIFIER (argspace_offset), - argspace_offset); + if (GET_CODE (x) != PARALLEL) + LOSE_AND_RETURN ("invalid operand for 'o' modifier", x); - /* Make sure we only do this once. */ - argspace_offset = 0; - } + /* The second item can be (set reg (plus reg const)) to denote a + postincrement. */ + regno + = (GET_CODE (SET_SRC (XVECEXP (x, 0, 1))) == PLUS + ? XVECLEN (x, 0) - 2 + : XVECLEN (x, 0) - 1); - /* Flush previous non-movem:ed registers. */ - if (*save_last && file) - fprintf (file, save_last); - sprintf (save_last, "\tPop $%s\n", reg_names[regno]); + fprintf (file, "$%s", reg_names [regno]); } - - if (last_movem_reg != -1) - { - if (argspace_offset) - { - /* Adjust for the outgoing parameters area, if that's not - handled yet. */ - if (*save_last && file) - { - fprintf (file, save_last); - *save_last = 0; - } - - if (file) - fprintf (file, "\tAdd%s %d,$sp\n", - ADDITIVE_SIZE_MODIFIER (argspace_offset), - argspace_offset); - argspace_offset = 0; - } - /* Flush previous non-movem:ed registers. */ - else if (*save_last && file) - fprintf (file, save_last); - sprintf (save_last, "\tmovem [$sp+],$%s\n", reg_names[last_movem_reg]); - } - - /* Restore frame pointer if necessary. */ - if (frame_pointer_needed) - { - if (*save_last && file) - fprintf (file, save_last); - - if (file) - fprintf (file, "\tmove.d $%s,$sp\n", - reg_names[FRAME_POINTER_REGNUM]); - sprintf (save_last, "\tPop $%s\n", - reg_names[FRAME_POINTER_REGNUM]); - } - else - { - /* If there was no frame-pointer to restore sp from, we must - explicitly deallocate local variables. */ - - /* Handle space for outgoing parameters that hasn't been handled - yet. */ - size += argspace_offset; - - if (size) - { - if (*save_last && file) - fprintf (file, save_last); - - sprintf (save_last, "\tadd%s %d,$sp\n", - ADDITIVE_SIZE_MODIFIER (size), size); - } - - /* If the size was not in the range for a "quick", we must flush - it here. */ - if (size > 63) - { - if (file) - fprintf (file, save_last); - *save_last = 0; - } - } - - /* If this function has no pushed register parameters - (stdargs/varargs), and if it is not a leaf function, then we can - just jump-return here. */ - if (return_address_on_stack && pretend == 0) - { - if (*save_last && file) - fprintf (file, save_last); - *save_last = 0; - - if (file) - { - if (current_function_calls_eh_return) - { - /* The installed EH-return address is in *this* frame, so we - need to pop it before we return. */ - fprintf (file, "\tpop $srp\n"); - fprintf (file, "\tret\n"); - fprintf (file, "\tadd.d $%s,$sp\n", reg_names[CRIS_STACKADJ_REG]); - } - else - fprintf (file, "\tJump [$sp+]\n"); - - /* Do a sanity check to avoid generating invalid code. */ - if (current_function_epilogue_delay_list) - internal_error ("allocated but unused delay list in epilogue"); - } return; - } - - /* Rather than add current_function_calls_eh_return conditions - everywhere in the following code (and not be able to test it - thoroughly), assert the assumption that all usage of - __builtin_eh_return are handled above. */ - if (current_function_calls_eh_return) - internal_error ("unexpected function type needing stack adjustment for\ - __builtin_eh_return"); - - /* If we pushed some register parameters, then adjust the stack for - them. */ - if (pretend) - { - /* Since srp is stored on the way, we need to restore it first. */ - if (return_address_on_stack) - { - if (*save_last && file) - fprintf (file, save_last); - *save_last = 0; - - if (file) - fprintf (file, "\tpop $srp\n"); - } - - if (*save_last && file) - fprintf (file, save_last); - - sprintf (save_last, "\tadd%s %d,$sp\n", - ADDITIVE_SIZE_MODIFIER (pretend), pretend); - } - - /* Here's where we have a delay-slot we need to fill. */ - if (file && current_function_epilogue_delay_list) - { - /* If gcc has allocated an insn for the epilogue delay slot, but - things were arranged so we now thought we could do it - ourselves, don't forget to flush that insn. */ - if (*save_last) - fprintf (file, save_last); - - fprintf (file, "\tRet\n"); - - /* Output the delay-slot-insn the mandated way. */ - final_scan_insn (XEXP (current_function_epilogue_delay_list, 0), - file, 1, -2, 1); - } - else if (file) - { - fprintf (file, "\tRet\n"); - - /* If the GCC did not do it, we have to use whatever insn we have, - or a nop. */ - if (*save_last) - fprintf (file, save_last); - else - fprintf (file, "\tnOp\n"); - } -} - -/* The PRINT_OPERAND worker. */ - -void -cris_print_operand (file, x, code) - FILE *file; - rtx x; - int code; -{ - rtx operand = x; - - /* Size-strings corresponding to MULT expressions. */ - static const char *mults[] = { "BAD:0", ".b", ".w", "BAD:3", ".d" }; - - /* New code entries should just be added to the switch below. If - handling is finished, just return. If handling was just a - modification of the operand, the modified operand should be put in - "operand", and then do a break to let default handling - (zero-modifier) output the operand. */ - switch (code) - { - case 'b': - /* Print the unsigned supplied integer as if it was signed - and < 0, i.e print 255 or 65535 as -1, 254, 65534 as -2, etc. */ - if (GET_CODE (x) != CONST_INT - || ! CONST_OK_FOR_LETTER_P (INTVAL (x), 'O')) - LOSE_AND_RETURN ("invalid operand for 'b' modifier", x); - fprintf (file, "%d", INTVAL (x)| (INTVAL (x) <= 255 ? ~255 : ~65535)); - return; + case 'O': + { + /* A similar movem modifier; output the memory operand. */ + rtx addr; - case 'x': - /* Print assembler code for operator. */ - fprintf (file, "%s", cris_op_str (operand)); - return; + if (GET_CODE (x) != PARALLEL) + LOSE_AND_RETURN ("invalid operand for 'O' modifier", x); - case 'v': - /* Print the operand without the PIC register. */ - if (! flag_pic || ! CONSTANT_P (x) || ! cris_gotless_symbol (x)) - LOSE_AND_RETURN ("invalid operand for 'v' modifier", x); - cris_pic_sympart_only++; - cris_output_addr_const (file, x); - cris_pic_sympart_only--; - return; + /* The lowest mem operand is in the first item, but perhaps it + needs to be output as postincremented. */ + addr = MEM_P (SET_SRC (XVECEXP (x, 0, 0))) + ? XEXP (SET_SRC (XVECEXP (x, 0, 0)), 0) + : XEXP (SET_DEST (XVECEXP (x, 0, 0)), 0); - case 'P': - /* Print the PIC register. Applied to a GOT-less PIC symbol for - sanity. */ - if (! flag_pic || ! CONSTANT_P (x) || ! cris_gotless_symbol (x)) - LOSE_AND_RETURN ("invalid operand for 'P' modifier", x); - fprintf (file, "$%s", reg_names [PIC_OFFSET_TABLE_REGNUM]); + /* The second item can be a (set reg (plus reg const)) to denote + a modification. */ + if (GET_CODE (SET_SRC (XVECEXP (x, 0, 1))) == PLUS) + { + /* It's a post-increment, if the address is a naked (reg). */ + if (REG_P (addr)) + addr = gen_rtx_POST_INC (SImode, addr); + else + { + /* Otherwise, it's a side-effect; RN=RN+M. */ + fprintf (file, "[$%s=$%s%s%d]", + reg_names [REGNO (SET_DEST (XVECEXP (x, 0, 1)))], + reg_names [REGNO (XEXP (addr, 0))], + INTVAL (XEXP (addr, 1)) < 0 ? "" : "+", + (int) INTVAL (XEXP (addr, 1))); + return; + } + } + output_address (addr); + } return; case 'p': /* Adjust a power of two to its log2. */ - if (GET_CODE (x) != CONST_INT || exact_log2 (INTVAL (x)) < 0 ) + if (!CONST_INT_P (x) || exact_log2 (INTVAL (x)) < 0 ) LOSE_AND_RETURN ("invalid operand for 'p' modifier", x); fprintf (file, "%d", exact_log2 (INTVAL (x))); return; @@ -1317,7 +763,7 @@ cris_print_operand (file, x, code) respectively. This modifier also terminates the inhibiting effects of the 'x' modifier. */ cris_output_insn_is_bound = 0; - if (GET_MODE (x) == VOIDmode && GET_CODE (x) == CONST_INT) + if (GET_MODE (x) == VOIDmode && CONST_INT_P (x)) { if (INTVAL (x) >= 0) { @@ -1344,13 +790,23 @@ cris_print_operand (file, x, code) case 'z': /* Const_int: print b for -127 <= x <= 255, - w for -32768 <= x <= 65535, else abort. */ - if (GET_CODE (x) != CONST_INT + w for -32768 <= x <= 65535, else die. */ + if (!CONST_INT_P (x) || INTVAL (x) < -32768 || INTVAL (x) > 65535) LOSE_AND_RETURN ("invalid operand for 'z' modifier", x); putc (INTVAL (x) >= -128 && INTVAL (x) <= 255 ? 'b' : 'w', file); return; + case 'Z': + /* If this is a GOT-symbol, print the size-letter corresponding to + -fpic/-fPIC. For everything else, print "d". */ + putc ((flag_pic == 1 + && GET_CODE (x) == CONST + && GET_CODE (XEXP (x, 0)) == UNSPEC + && XINT (XEXP (x, 0), 1) == CRIS_UNSPEC_GOTREAD) + ? 'w' : 'd', file); + return; + case '#': /* Output a 'nop' if there's nothing for the delay slot. This method stolen from the sparc files. */ @@ -1358,23 +814,46 @@ cris_print_operand (file, x, code) fputs ("\n\tnop", file); return; + case '!': + /* Output directive for alignment padded with "nop" insns. + Optimizing for size, it's plain 4-byte alignment, otherwise we + align the section to a cache-line (32 bytes) and skip at max 2 + bytes, i.e. we skip if it's the last insn on a cache-line. The + latter is faster by a small amount (for two test-programs 99.6% + and 99.9%) and larger by a small amount (ditto 100.1% and + 100.2%). This is supposed to be the simplest yet performance- + wise least intrusive way to make sure the immediately following + (supposed) muls/mulu insn isn't located at the end of a + cache-line. */ + if (TARGET_MUL_BUG) + fputs (optimize_size + ? ".p2alignw 2,0x050f\n\t" + : ".p2alignw 5,0x050f,2\n\t", file); + return; + + case ':': + /* The PIC register. */ + if (! flag_pic) + internal_error ("invalid use of ':' modifier"); + fprintf (file, "$%s", reg_names [PIC_OFFSET_TABLE_REGNUM]); + return; + case 'H': /* Print high (most significant) part of something. */ switch (GET_CODE (operand)) { case CONST_INT: - if (HOST_BITS_PER_WIDE_INT == 32) - /* Sign-extension from a normal int to a long long. */ - fprintf (file, INTVAL (operand) < 0 ? "-1" : "0"); - else - fprintf (file, "0x%x", (unsigned int)(INTVAL (x) >> 31 >> 1)); + /* If we're having 64-bit HOST_WIDE_INTs, the whole (DImode) + value is kept here, and so may be other than 0 or -1. */ + fprintf (file, HOST_WIDE_INT_PRINT_DEC, + INTVAL (operand_subword (operand, 1, 0, DImode))); return; case CONST_DOUBLE: /* High part of a long long constant. */ if (GET_MODE (operand) == VOIDmode) { - fprintf (file, "0x%x", CONST_DOUBLE_HIGH (x)); + fprintf (file, HOST_WIDE_INT_PRINT_HEX, CONST_DOUBLE_HIGH (x)); return; } else @@ -1382,8 +861,12 @@ cris_print_operand (file, x, code) case REG: /* Print reg + 1. Check that there's not an attempt to print - high-parts of registers like stack-pointer or higher. */ - if (REGNO (operand) > STACK_POINTER_REGNUM - 2) + high-parts of registers like stack-pointer or higher, except + for SRP (where the "high part" is MOF). */ + if (REGNO (operand) > STACK_POINTER_REGNUM - 2 + && (REGNO (operand) != CRIS_SRP_REGNUM + || CRIS_SRP_REGNUM + 1 != CRIS_MOF_REGNUM + || fixed_regs[CRIS_MOF_REGNUM] != 0)) LOSE_AND_RETURN ("bad register", operand); fprintf (file, "$%s", reg_names[REGNO (operand) + 1]); return; @@ -1417,11 +900,17 @@ cris_print_operand (file, x, code) break; case 'e': + /* Like 'E', but ignore state set by 'x'. FIXME: Use code + iterators and attributes in cris.md to avoid the need for %x + and %E (and %e) and state passed between those modifiers. */ + cris_output_insn_is_bound = 0; + /* FALL THROUGH. */ + case 'E': /* Print 's' if operand is SIGN_EXTEND or 'u' if ZERO_EXTEND unless cris_output_insn_is_bound is nonzero. */ if (GET_CODE (operand) != SIGN_EXTEND && GET_CODE (operand) != ZERO_EXTEND - && GET_CODE (operand) != CONST_INT) + && !CONST_INT_P (operand)) LOSE_AND_RETURN ("invalid operand for 'e' modifier", x); if (cris_output_insn_is_bound) @@ -1431,7 +920,7 @@ cris_print_operand (file, x, code) } putc (GET_CODE (operand) == SIGN_EXTEND - || (GET_CODE (operand) == CONST_INT && INTVAL (operand) < 0) + || (CONST_INT_P (operand) && INTVAL (operand) < 0) ? 's' : 'u', file); return; @@ -1447,12 +936,12 @@ cris_print_operand (file, x, code) /* Print the least significant part of operand. */ if (GET_CODE (operand) == CONST_DOUBLE) { - fprintf (file, "0x%x", CONST_DOUBLE_LOW (x)); + fprintf (file, HOST_WIDE_INT_PRINT_HEX, CONST_DOUBLE_LOW (x)); return; } - else if (HOST_BITS_PER_WIDE_INT > 32 && GET_CODE (operand) == CONST_INT) + else if (HOST_BITS_PER_WIDE_INT > 32 && CONST_INT_P (operand)) { - fprintf (file, "0x%x", + fprintf (file, HOST_WIDE_INT_PRINT_HEX, INTVAL (x) & ((unsigned int) 0x7fffffff * 2 + 1)); return; } @@ -1463,15 +952,30 @@ cris_print_operand (file, x, code) case 'A': /* When emitting an add for the high part of a DImode constant, we want to use addq for 0 and adds.w for -1. */ - if (GET_CODE (operand) != CONST_INT) + if (!CONST_INT_P (operand)) LOSE_AND_RETURN ("invalid operand for 'A' modifier", x); fprintf (file, INTVAL (operand) < 0 ? "adds.w" : "addq"); return; + case 'd': + /* If this is a GOT symbol, force it to be emitted as :GOT and + :GOTPLT regardless of -fpic (i.e. not as :GOT16, :GOTPLT16). + Avoid making this too much of a special case. */ + if (flag_pic == 1 && CONSTANT_P (operand)) + { + int flag_pic_save = flag_pic; + + flag_pic = 2; + cris_output_addr_const (file, operand); + flag_pic = flag_pic_save; + return; + } + break; + case 'D': /* When emitting an sub for the high part of a DImode constant, we want to use subq for 0 and subs.w for -1. */ - if (GET_CODE (operand) != CONST_INT) + if (!CONST_INT_P (operand)) LOSE_AND_RETURN ("invalid operand for 'D' modifier", x); fprintf (file, INTVAL (operand) < 0 ? "subs.w" : "subq"); return; @@ -1485,11 +989,22 @@ cris_print_operand (file, x, code) case 'T': /* Print the size letter for an operand to a MULT, which must be a const_int with a suitable value. */ - if (GET_CODE (operand) != CONST_INT || INTVAL (operand) > 4) + if (!CONST_INT_P (operand) || INTVAL (operand) > 4) LOSE_AND_RETURN ("invalid operand for 'T' modifier", x); fprintf (file, "%s", mults[INTVAL (operand)]); return; + case 'u': + /* Print "u.w" if a GOT symbol and flag_pic == 1, else ".d". */ + if (flag_pic == 1 + && GET_CODE (operand) == CONST + && GET_CODE (XEXP (operand, 0)) == UNSPEC + && XINT (XEXP (operand, 0), 1) == CRIS_UNSPEC_GOTREAD) + fprintf (file, "u.w"); + else + fprintf (file, ".d"); + return; + case 0: /* No code, print as usual. */ break; @@ -1502,7 +1017,10 @@ cris_print_operand (file, x, code) switch (GET_CODE (operand)) { case REG: - if (REGNO (operand) > 15) + if (REGNO (operand) > 15 + && REGNO (operand) != CRIS_MOF_REGNUM + && REGNO (operand) != CRIS_SRP_REGNUM + && REGNO (operand) != CRIS_CC0_REGNUM) internal_error ("internal error: bad register: %d", REGNO (operand)); fprintf (file, "$%s", reg_names[REGNO (operand)]); return; @@ -1532,9 +1050,7 @@ cris_print_operand (file, x, code) return; case UNSPEC: - ASSERT_PLT_UNSPEC (operand); /* Fall through. */ - case CONST: cris_output_addr_const (file, operand); return; @@ -1543,14 +1059,14 @@ cris_print_operand (file, x, code) case ASHIFT: { /* For a (MULT (reg X) const_int) we output "rX.S". */ - int i = GET_CODE (XEXP (operand, 1)) == CONST_INT + int i = CONST_INT_P (XEXP (operand, 1)) ? INTVAL (XEXP (operand, 1)) : INTVAL (XEXP (operand, 0)); - rtx reg = GET_CODE (XEXP (operand, 1)) == CONST_INT + rtx reg = CONST_INT_P (XEXP (operand, 1)) ? XEXP (operand, 0) : XEXP (operand, 1); - if (GET_CODE (reg) != REG - || (GET_CODE (XEXP (operand, 0)) != CONST_INT - && GET_CODE (XEXP (operand, 1)) != CONST_INT)) + if (!REG_P (reg) + || (!CONST_INT_P (XEXP (operand, 0)) + && !CONST_INT_P (XEXP (operand, 1)))) LOSE_AND_RETURN ("unexpected multiplicative operand", x); cris_print_base (reg, file); @@ -1578,9 +1094,7 @@ cris_print_operand (file, x, code) /* The PRINT_OPERAND_ADDRESS worker. */ void -cris_print_operand_address (file, x) - FILE *file; - rtx x; +cris_print_operand_address (FILE *file, rtx x) { /* All these were inside MEM:s so output indirection characters. */ putc ('[', file); @@ -1608,7 +1122,7 @@ cris_print_operand_address (file, x) else LOSE_AND_RETURN ("unrecognized address", x); } - else if (GET_CODE (x) == MEM) + else if (MEM_P (x)) { /* A DIP. Output more indirection characters. */ putc ('[', file); @@ -1630,9 +1144,7 @@ cris_print_operand_address (file, x) initial-value machinery. */ rtx -cris_return_addr_rtx (count, frameaddr) - int count; - rtx frameaddr ATTRIBUTE_UNUSED; +cris_return_addr_rtx (int count, rtx frameaddr ATTRIBUTE_UNUSED) { cfun->machine->needs_return_address_on_stack = 1; @@ -1644,41 +1156,57 @@ cris_return_addr_rtx (count, frameaddr) : NULL_RTX; } +/* Accessor used in cris.md:return because cfun->machine isn't available + there. */ + +bool +cris_return_address_on_stack (void) +{ + return df_regs_ever_live_p (CRIS_SRP_REGNUM) + || cfun->machine->needs_return_address_on_stack; +} + +/* Accessor used in cris.md:return because cfun->machine isn't available + there. */ + +bool +cris_return_address_on_stack_for_return (void) +{ + return cfun->machine->return_type == CRIS_RETINSN_RET ? false + : cris_return_address_on_stack (); +} + /* This used to be the INITIAL_FRAME_POINTER_OFFSET worker; now only handles FP -> SP elimination offset. */ static int -cris_initial_frame_pointer_offset () +cris_initial_frame_pointer_offset (void) { int regno; /* Initial offset is 0 if we don't have a frame pointer. */ int offs = 0; + bool got_really_used = false; + + if (crtl->uses_pic_offset_table) + { + push_topmost_sequence (); + got_really_used + = reg_used_between_p (pic_offset_table_rtx, get_insns (), + NULL_RTX); + pop_topmost_sequence (); + } /* And 4 for each register pushed. */ for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) - if ((((regs_ever_live[regno] - && !call_used_regs[regno]) - || (regno == (int) PIC_OFFSET_TABLE_REGNUM - && (current_function_uses_pic_offset_table - /* It is saved anyway, if there would be a gap. */ - || (flag_pic - && regs_ever_live[regno + 1] - && !call_used_regs[regno + 1])))) - && (regno != FRAME_POINTER_REGNUM || !frame_pointer_needed) - && regno != CRIS_SRP_REGNUM) - || (current_function_calls_eh_return - && (regno == EH_RETURN_DATA_REGNO (0) - || regno == EH_RETURN_DATA_REGNO (1) - || regno == EH_RETURN_DATA_REGNO (2) - || regno == EH_RETURN_DATA_REGNO (3)))) + if (cris_reg_saved_in_regsave_area (regno, got_really_used)) offs += 4; /* And then, last, we add the locals allocated. */ offs += get_frame_size (); /* And more; the accumulated args size. */ - offs += current_function_outgoing_args_size; + offs += crtl->outgoing_args_size; /* Then round it off, in case we use aligned stack. */ if (TARGET_STACK_ALIGN) @@ -1693,20 +1221,16 @@ cris_initial_frame_pointer_offset () and imaginary arg pointer. */ int -cris_initial_elimination_offset (fromreg, toreg) - int fromreg; - int toreg; +cris_initial_elimination_offset (int fromreg, int toreg) { int fp_sp_offset = cris_initial_frame_pointer_offset (); /* We should be able to use regs_ever_live and related prologue information here, or alpha should not as well. */ - int return_address_on_stack - = regs_ever_live[CRIS_SRP_REGNUM] - || cfun->machine->needs_return_address_on_stack != 0; + bool return_address_on_stack = cris_return_address_on_stack (); - /* Here we act as if the frame-pointer is needed. */ + /* Here we act as if the frame-pointer were needed. */ int ap_fp_offset = 4 + (return_address_on_stack ? 4 : 0); if (fromreg == ARG_POINTER_REGNUM @@ -1724,366 +1248,636 @@ cris_initial_elimination_offset (fromreg, toreg) && toreg == STACK_POINTER_REGNUM) return ap_fp_offset + fp_sp_offset - 4; - abort (); + gcc_unreachable (); } -/* This function looks into the pattern to see how this insn affects - condition codes. +/* Worker function for LEGITIMIZE_RELOAD_ADDRESS. */ - Used when to eliminate test insns before a condition-code user, - such as a "scc" insn or a conditional branch. This includes - checking if the entities that cc was updated by, are changed by the - operation. +bool +cris_reload_address_legitimized (rtx x, + enum machine_mode mode ATTRIBUTE_UNUSED, + int opnum ATTRIBUTE_UNUSED, + int itype, + int ind_levels ATTRIBUTE_UNUSED) +{ + enum reload_type type = itype; + rtx op0, op1; + rtx *op0p; + rtx *op1p; - Currently a jumble of the old peek-inside-the-insn and the newer - check-cc-attribute methods. */ + if (GET_CODE (x) != PLUS) + return false; -void -cris_notice_update_cc (exp, insn) - rtx exp; - rtx insn; -{ - /* Check if user specified "-mcc-init" as a bug-workaround. FIXME: - TARGET_CCINIT does not work; we must set CC_REVERSED as below. - Several test-cases will otherwise fail, for example - gcc.c-torture/execute/20000217-1.c -O0 and -O1. */ - if (TARGET_CCINIT) - { - CC_STATUS_INIT; - return; - } + if (TARGET_V32) + return false; - /* Slowly, we're converting to using attributes to control the setting - of condition-code status. */ - switch (get_attr_cc (insn)) + op0 = XEXP (x, 0); + op0p = &XEXP (x, 0); + op1 = XEXP (x, 1); + op1p = &XEXP (x, 1); + + if (!REG_P (op1)) + return false; + + if (GET_CODE (op0) == SIGN_EXTEND && MEM_P (XEXP (op0, 0))) { - case CC_NONE: - /* Even if it is "none", a setting may clobber a previous - cc-value, so check. */ - if (GET_CODE (exp) == SET) + rtx op00 = XEXP (op0, 0); + rtx op000 = XEXP (op00, 0); + rtx *op000p = &XEXP (op00, 0); + + if ((GET_MODE (op00) == HImode || GET_MODE (op00) == QImode) + && (REG_P (op000) + || (GET_CODE (op000) == POST_INC && REG_P (XEXP (op000, 0))))) { - if (cc_status.value1 - && cris_reg_overlap_mentioned_p (SET_DEST (exp), - cc_status.value1)) - cc_status.value1 = 0; + bool something_reloaded = false; + + if (GET_CODE (op000) == POST_INC + && REG_P (XEXP (op000, 0)) + && REGNO (XEXP (op000, 0)) > CRIS_LAST_GENERAL_REGISTER) + /* No, this gets too complicated and is too rare to care + about trying to improve on the general code Here. + As the return-value is an all-or-nothing indicator, we + punt on the other register too. */ + return false; + + if ((REG_P (op000) + && REGNO (op000) > CRIS_LAST_GENERAL_REGISTER)) + { + /* The address of the inner mem is a pseudo or wrong + reg: reload that. */ + push_reload (op000, NULL_RTX, op000p, NULL, GENERAL_REGS, + GET_MODE (x), VOIDmode, 0, 0, opnum, type); + something_reloaded = true; + } - if (cc_status.value2 - && cris_reg_overlap_mentioned_p (SET_DEST (exp), - cc_status.value2)) - cc_status.value2 = 0; + if (REGNO (op1) > CRIS_LAST_GENERAL_REGISTER) + { + /* Base register is a pseudo or wrong reg: reload it. */ + push_reload (op1, NULL_RTX, op1p, NULL, GENERAL_REGS, + GET_MODE (x), VOIDmode, 0, 0, + opnum, type); + something_reloaded = true; + } + + gcc_assert (something_reloaded); + + return true; } - return; + } - case CC_CLOBBER: - CC_STATUS_INIT; - break; + return false; +} - case CC_NORMAL: - /* Which means, for: - (set (cc0) (...)): - CC is (...). +/* Worker function for REGISTER_MOVE_COST. */ + +int +cris_register_move_cost (enum machine_mode mode ATTRIBUTE_UNUSED, + enum reg_class from, enum reg_class to) +{ + if (!TARGET_V32) + { + /* Pretend that classes that we don't support are ALL_REGS, so + we give them the highest cost. */ + if (from != SPECIAL_REGS && from != MOF_REGS + && from != GENERAL_REGS && from != GENNONACR_REGS) + from = ALL_REGS; + + if (to != SPECIAL_REGS && to != MOF_REGS + && to != GENERAL_REGS && to != GENNONACR_REGS) + to = ALL_REGS; + } + + /* Can't move to and from a SPECIAL_REGS register, so we have to say + their move cost within that class is higher. How about 7? That's 3 + for a move to a GENERAL_REGS register, 3 for the move from the + GENERAL_REGS register, and 1 for the increased register pressure. + Also, it's higher than the memory move cost, which is in order. + We also do this for ALL_REGS, since we don't want that class to be + preferred (even to memory) at all where GENERAL_REGS doesn't fit. + Whenever it's about to be used, it's for SPECIAL_REGS. If we don't + present a higher cost for ALL_REGS than memory, a SPECIAL_REGS may be + used when a GENERAL_REGS should be used, even if there are call-saved + GENERAL_REGS left to allocate. This is because the fall-back when + the most preferred register class isn't available, isn't the next + (or next good) wider register class, but the *most widest* register + class. */ + + if ((reg_classes_intersect_p (from, SPECIAL_REGS) + && reg_classes_intersect_p (to, SPECIAL_REGS)) + || from == ALL_REGS || to == ALL_REGS) + return 7; + + if (reg_classes_intersect_p (from, SPECIAL_REGS) + || reg_classes_intersect_p (to, SPECIAL_REGS)) + return 3; + + return 2; +} + +/* Worker for cris_notice_update_cc; handles the "normal" cases. + FIXME: this code is historical; its functionality should be + refactored to look at insn attributes and moved to + cris_notice_update_cc. Except, we better lose cc0 entirely. */ - (set (reg) (...)): - CC is (reg) and (...) - unless (...) is 0, then CC does not change. - CC_NO_OVERFLOW unless (...) is reg or mem. +static void +cris_normal_notice_update_cc (rtx exp, rtx insn) +{ + /* "Normal" means, for: + (set (cc0) (...)): + CC is (...). - (set (mem) (...)): - CC does not change. + (set (reg) (...)): + CC is (reg) and (...) - unless (...) is 0 or reg is a special + register or (v32 and (...) is -32..-1), then CC does not change. + CC_NO_OVERFLOW unless (...) is reg or mem. - (set (pc) (...)): - CC does not change. + (set (mem) (...)): + CC does not change. - (parallel - (set (reg1) (mem (bdap/biap))) - (set (reg2) (bdap/biap))): - CC is (reg1) and (mem (reg2)) + (set (pc) (...)): + CC does not change. - (parallel - (set (mem (bdap/biap)) (reg1)) [or 0] - (set (reg2) (bdap/biap))): - CC does not change. + (parallel + (set (reg1) (mem (bdap/biap))) + (set (reg2) (bdap/biap))): + CC is (reg1) and (mem (reg2)) - (where reg and mem includes strict_low_parts variants thereof) + (parallel + (set (mem (bdap/biap)) (reg1)) [or 0] + (set (reg2) (bdap/biap))): + CC does not change. - For all others, assume CC is clobbered. - Note that we do not have to care about setting CC_NO_OVERFLOW, - since the overflow flag is set to 0 (i.e. right) for - instructions where it does not have any sane sense, but where - other flags have meanings. (This includes shifts; the carry is - not set by them). + (where reg and mem includes strict_low_parts variants thereof) - Note that there are other parallel constructs we could match, - but we don't do that yet. */ + For all others, assume CC is clobbered. + Note that we do not have to care about setting CC_NO_OVERFLOW, + since the overflow flag is set to 0 (i.e. right) for + instructions where it does not have any sane sense, but where + other flags have meanings. (This includes shifts; the carry is + not set by them). - if (GET_CODE (exp) == SET) + Note that there are other parallel constructs we could match, + but we don't do that yet. */ + + if (GET_CODE (exp) == SET) + { + /* FIXME: Check when this happens. It looks like we should + actually do a CC_STATUS_INIT here to be safe. */ + if (SET_DEST (exp) == pc_rtx) + return; + + /* Record CC0 changes, so we do not have to output multiple + test insns. */ + if (SET_DEST (exp) == cc0_rtx) { - /* FIXME: Check when this happens. It looks like we should - actually do a CC_STATUS_INIT here to be safe. */ - if (SET_DEST (exp) == pc_rtx) - return; + CC_STATUS_INIT; + cc_status.value1 = SET_SRC (exp); - /* Record CC0 changes, so we do not have to output multiple - test insns. */ - if (SET_DEST (exp) == cc0_rtx) + /* Handle flags for the special btstq on one bit. */ + if (GET_CODE (SET_SRC (exp)) == ZERO_EXTRACT + && XEXP (SET_SRC (exp), 1) == const1_rtx) { - cc_status.value1 = SET_SRC (exp); - cc_status.value2 = 0; - - /* Handle flags for the special btstq on one bit. */ - if (GET_CODE (SET_SRC (exp)) == ZERO_EXTRACT - && XEXP (SET_SRC (exp), 1) == const1_rtx) - { - if (GET_CODE (XEXP (SET_SRC (exp), 0)) == CONST_INT) - /* Using cmpq. */ - cc_status.flags = CC_INVERTED; - else - /* A one-bit btstq. */ - cc_status.flags = CC_Z_IN_NOT_N; - } + if (CONST_INT_P (XEXP (SET_SRC (exp), 0))) + /* Using cmpq. */ + cc_status.flags = CC_INVERTED; else - cc_status.flags = 0; - - if (GET_CODE (SET_SRC (exp)) == COMPARE) - { - if (!REG_P (XEXP (SET_SRC (exp), 0)) - && XEXP (SET_SRC (exp), 1) != const0_rtx) - /* For some reason gcc will not canonicalize compare - operations, reversing the sign by itself if - operands are in wrong order. */ - /* (But NOT inverted; eq is still eq.) */ - cc_status.flags = CC_REVERSED; - - /* This seems to be overlooked by gcc. FIXME: Check again. - FIXME: Is it really safe? */ - cc_status.value2 - = gen_rtx_MINUS (GET_MODE (SET_SRC (exp)), - XEXP (SET_SRC (exp), 0), - XEXP (SET_SRC (exp), 1)); - } - return; + /* A one-bit btstq. */ + cc_status.flags = CC_Z_IN_NOT_N; } - else if (REG_P (SET_DEST (exp)) - || (GET_CODE (SET_DEST (exp)) == STRICT_LOW_PART - && REG_P (XEXP (SET_DEST (exp), 0)))) + + if (GET_CODE (SET_SRC (exp)) == COMPARE) { - /* A register is set; normally CC is set to show that no - test insn is needed. Catch the exceptions. */ + if (!REG_P (XEXP (SET_SRC (exp), 0)) + && XEXP (SET_SRC (exp), 1) != const0_rtx) + /* For some reason gcc will not canonicalize compare + operations, reversing the sign by itself if + operands are in wrong order. */ + /* (But NOT inverted; eq is still eq.) */ + cc_status.flags = CC_REVERSED; + + /* This seems to be overlooked by gcc. FIXME: Check again. + FIXME: Is it really safe? */ + cc_status.value2 + = gen_rtx_MINUS (GET_MODE (SET_SRC (exp)), + XEXP (SET_SRC (exp), 0), + XEXP (SET_SRC (exp), 1)); + } + return; + } + else if (REG_P (SET_DEST (exp)) + || (GET_CODE (SET_DEST (exp)) == STRICT_LOW_PART + && REG_P (XEXP (SET_DEST (exp), 0)))) + { + /* A register is set; normally CC is set to show that no + test insn is needed. Catch the exceptions. */ - /* If not to cc0, then no "set"s in non-natural mode give - ok cc0... */ - if (GET_MODE_SIZE (GET_MODE (SET_DEST (exp))) > UNITS_PER_WORD - || GET_MODE_CLASS (GET_MODE (SET_DEST (exp))) == MODE_FLOAT) + /* If not to cc0, then no "set"s in non-natural mode give + ok cc0... */ + if (GET_MODE_SIZE (GET_MODE (SET_DEST (exp))) > UNITS_PER_WORD + || GET_MODE_CLASS (GET_MODE (SET_DEST (exp))) == MODE_FLOAT) + { + /* ... except add:s and sub:s in DImode. */ + if (GET_MODE (SET_DEST (exp)) == DImode + && (GET_CODE (SET_SRC (exp)) == PLUS + || GET_CODE (SET_SRC (exp)) == MINUS)) { - /* ... except add:s and sub:s in DImode. */ - if (GET_MODE (SET_DEST (exp)) == DImode - && (GET_CODE (SET_SRC (exp)) == PLUS - || GET_CODE (SET_SRC (exp)) == MINUS)) - { - cc_status.flags = 0; - cc_status.value1 = SET_DEST (exp); - cc_status.value2 = SET_SRC (exp); + CC_STATUS_INIT; + cc_status.value1 = SET_DEST (exp); + cc_status.value2 = SET_SRC (exp); - if (cris_reg_overlap_mentioned_p (cc_status.value1, - cc_status.value2)) - cc_status.value2 = 0; + if (cris_reg_overlap_mentioned_p (cc_status.value1, + cc_status.value2)) + cc_status.value2 = 0; - /* Add and sub may set V, which gets us - unoptimizable results in "gt" and "le" condition - codes. */ - cc_status.flags |= CC_NO_OVERFLOW; - - return; - } - } - else if (SET_SRC (exp) == const0_rtx) - { - /* There's no CC0 change when clearing a register or - memory. Just check for overlap. */ - if ((cc_status.value1 - && cris_reg_overlap_mentioned_p (SET_DEST (exp), - cc_status.value1))) - cc_status.value1 = 0; - - if ((cc_status.value2 - && cris_reg_overlap_mentioned_p (SET_DEST (exp), - cc_status.value2))) - cc_status.value2 = 0; - - return; - } - else - { - cc_status.flags = 0; - cc_status.value1 = SET_DEST (exp); - cc_status.value2 = SET_SRC (exp); - - if (cris_reg_overlap_mentioned_p (cc_status.value1, - cc_status.value2)) - cc_status.value2 = 0; - - /* Some operations may set V, which gets us + /* Add and sub may set V, which gets us unoptimizable results in "gt" and "le" condition codes. */ - if (GET_CODE (SET_SRC (exp)) == PLUS - || GET_CODE (SET_SRC (exp)) == MINUS - || GET_CODE (SET_SRC (exp)) == NEG) - cc_status.flags |= CC_NO_OVERFLOW; + cc_status.flags |= CC_NO_OVERFLOW; return; } } - else if (GET_CODE (SET_DEST (exp)) == MEM - || (GET_CODE (SET_DEST (exp)) == STRICT_LOW_PART - && GET_CODE (XEXP (SET_DEST (exp), 0)) == MEM)) + else if (SET_SRC (exp) == const0_rtx + || (REG_P (SET_SRC (exp)) + && (REGNO (SET_SRC (exp)) + > CRIS_LAST_GENERAL_REGISTER)) + || (TARGET_V32 + && GET_CODE (SET_SRC (exp)) == CONST_INT + && CRIS_CONST_OK_FOR_LETTER_P (INTVAL (SET_SRC (exp)), + 'I'))) { - /* When SET to MEM, then CC is not changed (except for - overlap). */ - if ((cc_status.value1 - && cris_reg_overlap_mentioned_p (SET_DEST (exp), - cc_status.value1))) + /* There's no CC0 change for this case. Just check + for overlap. */ + if (cc_status.value1 + && modified_in_p (cc_status.value1, insn)) cc_status.value1 = 0; - if ((cc_status.value2 - && cris_reg_overlap_mentioned_p (SET_DEST (exp), - cc_status.value2))) + if (cc_status.value2 + && modified_in_p (cc_status.value2, insn)) + cc_status.value2 = 0; + + return; + } + else + { + CC_STATUS_INIT; + cc_status.value1 = SET_DEST (exp); + cc_status.value2 = SET_SRC (exp); + + if (cris_reg_overlap_mentioned_p (cc_status.value1, + cc_status.value2)) cc_status.value2 = 0; + /* Some operations may set V, which gets us + unoptimizable results in "gt" and "le" condition + codes. */ + if (GET_CODE (SET_SRC (exp)) == PLUS + || GET_CODE (SET_SRC (exp)) == MINUS + || GET_CODE (SET_SRC (exp)) == NEG) + cc_status.flags |= CC_NO_OVERFLOW; + + /* For V32, nothing with a register destination sets + C and V usefully. */ + if (TARGET_V32) + cc_status.flags |= CC_NO_OVERFLOW; + return; } } - else if (GET_CODE (exp) == PARALLEL) + else if (MEM_P (SET_DEST (exp)) + || (GET_CODE (SET_DEST (exp)) == STRICT_LOW_PART + && MEM_P (XEXP (SET_DEST (exp), 0)))) + { + /* When SET to MEM, then CC is not changed (except for + overlap). */ + if (cc_status.value1 + && modified_in_p (cc_status.value1, insn)) + cc_status.value1 = 0; + + if (cc_status.value2 + && modified_in_p (cc_status.value2, insn)) + cc_status.value2 = 0; + + return; + } + } + else if (GET_CODE (exp) == PARALLEL) + { + if (GET_CODE (XVECEXP (exp, 0, 0)) == SET + && GET_CODE (XVECEXP (exp, 0, 1)) == SET + && REG_P (XEXP (XVECEXP (exp, 0, 1), 0))) { - if (GET_CODE (XVECEXP (exp, 0, 0)) == SET - && GET_CODE (XVECEXP (exp, 0, 1)) == SET - && REG_P (XEXP (XVECEXP (exp, 0, 1), 0))) + if (REG_P (XEXP (XVECEXP (exp, 0, 0), 0)) + && MEM_P (XEXP (XVECEXP (exp, 0, 0), 1))) { - if (REG_P (XEXP (XVECEXP (exp, 0, 0), 0)) - && GET_CODE (XEXP (XVECEXP (exp, 0, 0), 1)) == MEM) - { - /* For "move.S [rx=ry+o],rz", say CC reflects - value1=rz and value2=[rx] */ - cc_status.value1 = XEXP (XVECEXP (exp, 0, 0), 0); - cc_status.value2 - = gen_rtx_MEM (GET_MODE (XEXP (XVECEXP (exp, 0, 0), 0)), - XEXP (XVECEXP (exp, 0, 1), 0)); - cc_status.flags = 0; - - /* Huh? A side-effect cannot change the destination - register. */ - if (cris_reg_overlap_mentioned_p (cc_status.value1, - cc_status.value2)) - internal_error ("internal error: sideeffect-insn affecting main effect"); - return; - } - else if ((REG_P (XEXP (XVECEXP (exp, 0, 0), 1)) - || XEXP (XVECEXP (exp, 0, 0), 1) == const0_rtx) - && GET_CODE (XEXP (XVECEXP (exp, 0, 0), 0)) == MEM) - { - /* For "move.S rz,[rx=ry+o]" and "clear.S [rx=ry+o]", - say flags are not changed, except for overlap. */ - if (cc_status.value1 - && cris_reg_overlap_mentioned_p (XEXP - (XVECEXP - (exp, 0, 0), 0), - cc_status.value1)) - cc_status.value1 = 0; - - if (cc_status.value1 - && cris_reg_overlap_mentioned_p (XEXP - (XVECEXP - (exp, 0, 1), 0), - cc_status.value1)) - cc_status.value1 = 0; - - if (cc_status.value2 - && cris_reg_overlap_mentioned_p (XEXP - (XVECEXP - (exp, 0, 0), 0), - cc_status.value2)) - cc_status.value2 = 0; + CC_STATUS_INIT; + + /* For "move.S [rx=ry+o],rz", say CC reflects + value1=rz and value2=[rx] */ + cc_status.value1 = XEXP (XVECEXP (exp, 0, 0), 0); + cc_status.value2 + = replace_equiv_address (XEXP (XVECEXP (exp, 0, 0), 1), + XEXP (XVECEXP (exp, 0, 1), 0)); + + /* Huh? A side-effect cannot change the destination + register. */ + if (cris_reg_overlap_mentioned_p (cc_status.value1, + cc_status.value2)) + internal_error ("internal error: sideeffect-insn affecting main effect"); + + /* For V32, moves to registers don't set C and V. */ + if (TARGET_V32) + cc_status.flags |= CC_NO_OVERFLOW; + return; + } + else if ((REG_P (XEXP (XVECEXP (exp, 0, 0), 1)) + || XEXP (XVECEXP (exp, 0, 0), 1) == const0_rtx) + && MEM_P (XEXP (XVECEXP (exp, 0, 0), 0))) + { + /* For "move.S rz,[rx=ry+o]" and "clear.S [rx=ry+o]", + say flags are not changed, except for overlap. */ + if (cc_status.value1 + && modified_in_p (cc_status.value1, insn)) + cc_status.value1 = 0; - if (cc_status.value2 - && cris_reg_overlap_mentioned_p (XEXP - (XVECEXP - (exp, 0, 1), 0), - cc_status.value2)) - cc_status.value2 = 0; + if (cc_status.value2 + && modified_in_p (cc_status.value2, insn)) + cc_status.value2 = 0; - return; - } + return; } } - break; + } + + /* If we got here, the case wasn't covered by the code above. */ + CC_STATUS_INIT; +} + +/* This function looks into the pattern to see how this insn affects + condition codes. + + Used when to eliminate test insns before a condition-code user, + such as a "scc" insn or a conditional branch. This includes + checking if the entities that cc was updated by, are changed by the + operation. + + Currently a jumble of the old peek-inside-the-insn and the newer + check-cc-attribute methods. */ + +void +cris_notice_update_cc (rtx exp, rtx insn) +{ + enum attr_cc attrval = get_attr_cc (insn); + + /* Check if user specified "-mcc-init" as a bug-workaround. Remember + to still set CC_REVERSED as below, since that's required by some + compare insn alternatives. (FIXME: GCC should do this virtual + operand swap by itself.) A test-case that may otherwise fail is + gcc.c-torture/execute/20000217-1.c -O0 and -O1. */ + if (TARGET_CCINIT) + { + CC_STATUS_INIT; + + if (attrval == CC_REV) + cc_status.flags = CC_REVERSED; + return; + } + + /* Slowly, we're converting to using attributes to control the setting + of condition-code status. */ + switch (attrval) + { + case CC_NONE: + /* Even if it is "none", a setting may clobber a previous + cc-value, so check. */ + if (GET_CODE (exp) == SET) + { + if (cc_status.value1 + && modified_in_p (cc_status.value1, insn)) + cc_status.value1 = 0; + + if (cc_status.value2 + && modified_in_p (cc_status.value2, insn)) + cc_status.value2 = 0; + } + return; + + case CC_CLOBBER: + CC_STATUS_INIT; + return; + + case CC_REV: + case CC_NOOV32: + case CC_NORMAL: + cris_normal_notice_update_cc (exp, insn); + + /* The "test" insn doesn't clear (carry and) overflow on V32. We + can change bge => bpl and blt => bmi by passing on to the cc0 + user that V should not be considered; bgt and ble are taken + care of by other methods (see {tst,cmp}{si,hi,qi}). */ + if (attrval == CC_NOOV32 && TARGET_V32) + cc_status.flags |= CC_NO_OVERFLOW; + return; default: - /* Unknown cc_attr value. */ - abort (); + internal_error ("unknown cc_attr value"); } CC_STATUS_INIT; } /* Return != 0 if the return sequence for the current function is short, - like "ret" or "jump [sp+]". Prior to reloading, we can't tell how - many registers must be saved, so return 0 then. */ + like "ret" or "jump [sp+]". Prior to reloading, we can't tell if + registers must be saved, so return 0 then. */ -int -cris_simple_epilogue () +bool +cris_simple_epilogue (void) { - int regno; - int reglimit = STACK_POINTER_REGNUM; - int lastreg = -1; + unsigned int regno; + unsigned int reglimit = STACK_POINTER_REGNUM; + bool got_really_used = false; if (! reload_completed || frame_pointer_needed || get_frame_size () != 0 - || current_function_pretend_args_size - || current_function_args_size - || current_function_outgoing_args_size - || current_function_calls_eh_return - - /* Kludge for 3.1: when reorg changes branches to the return label - into return insns, it does not handle the case where there's a - delay list for the epilogue: it just drops the insns in - current_function_epilogue_delay_list on the floor, resulting in - invalid code. We kludge around it in that case by saying that - we don't have a simple enough epilogue to use return insns. */ - || current_function_epilogue_delay_list != NULL + || crtl->args.pretend_args_size + || crtl->args.size + || crtl->outgoing_args_size + || crtl->calls_eh_return /* If we're not supposed to emit prologue and epilogue, we must not emit return-type instructions. */ || !TARGET_PROLOGUE_EPILOGUE) - return 0; + return false; + + /* Can't return from stacked return address with v32. */ + if (TARGET_V32 && cris_return_address_on_stack ()) + return false; + + if (crtl->uses_pic_offset_table) + { + push_topmost_sequence (); + got_really_used + = reg_used_between_p (pic_offset_table_rtx, get_insns (), NULL_RTX); + pop_topmost_sequence (); + } - /* We allow a "movem [sp+],rN" to sit in front if the "jump [sp+]" or - in the delay-slot of the "ret". */ + /* No simple epilogue if there are saved registers. */ for (regno = 0; regno < reglimit; regno++) - if ((regs_ever_live[regno] && ! call_used_regs[regno]) - || (regno == (int) PIC_OFFSET_TABLE_REGNUM - && (current_function_uses_pic_offset_table - /* It is saved anyway, if there would be a gap. */ - || (flag_pic - && regs_ever_live[regno + 1] - && !call_used_regs[regno + 1])))) + if (cris_reg_saved_in_regsave_area (regno, got_really_used)) + return false; + + return true; +} + +/* Expand a return insn (just one insn) marked as using SRP or stack + slot depending on parameter ON_STACK. */ + +void +cris_expand_return (bool on_stack) +{ + /* FIXME: emit a parallel with a USE for SRP or the stack-slot, to + tell "ret" from "jump [sp+]". Some, but not all, other parts of + GCC expect just (return) to do the right thing when optimizing, so + we do that until they're fixed. Currently, all return insns in a + function must be the same (not really a limiting factor) so we need + to check that it doesn't change half-way through. */ + emit_jump_insn (gen_rtx_RETURN (VOIDmode)); + + CRIS_ASSERT (cfun->machine->return_type != CRIS_RETINSN_RET || !on_stack); + CRIS_ASSERT (cfun->machine->return_type != CRIS_RETINSN_JUMP || on_stack); + + cfun->machine->return_type + = on_stack ? CRIS_RETINSN_JUMP : CRIS_RETINSN_RET; +} + +/* Compute a (partial) cost for rtx X. Return true if the complete + cost has been computed, and false if subexpressions should be + scanned. In either case, *TOTAL contains the cost result. */ + +static bool +cris_rtx_costs (rtx x, int code, int outer_code, int *total, + bool speed) +{ + switch (code) + { + case CONST_INT: { - if (lastreg != regno - 1) - return 0; - lastreg = regno; + HOST_WIDE_INT val = INTVAL (x); + if (val == 0) + *total = 0; + else if (val < 32 && val >= -32) + *total = 1; + /* Eight or 16 bits are a word and cycle more expensive. */ + else if (val <= 32767 && val >= -32768) + *total = 2; + /* A 32-bit constant (or very seldom, unsigned 16 bits) costs + another word. FIXME: This isn't linear to 16 bits. */ + else + *total = 4; + return true; } - return 1; + case LABEL_REF: + *total = 6; + return true; + + case CONST: + case SYMBOL_REF: + *total = 6; + return true; + + case CONST_DOUBLE: + if (x != CONST0_RTX (GET_MODE (x) == VOIDmode ? DImode : GET_MODE (x))) + *total = 12; + else + /* Make 0.0 cheap, else test-insns will not be used. */ + *total = 0; + return true; + + case MULT: + /* If we have one arm of an ADDI, make sure it gets the cost of + one insn, i.e. zero cost for this operand, and just the cost + of the PLUS, as the insn is created by combine from a PLUS + and an ASHIFT, and the MULT cost below would make the + combined value be larger than the separate insns. The insn + validity is checked elsewhere by combine. + + FIXME: this case is a stop-gap for 4.3 and 4.4, this whole + function should be rewritten. */ + if (outer_code == PLUS && BIAP_INDEX_P (x)) + { + *total = 0; + return true; + } + + /* Identify values that are no powers of two. Powers of 2 are + taken care of already and those values should not be changed. */ + if (!CONST_INT_P (XEXP (x, 1)) + || exact_log2 (INTVAL (XEXP (x, 1)) < 0)) + { + /* If we have a multiply insn, then the cost is between + 1 and 2 "fast" instructions. */ + if (TARGET_HAS_MUL_INSNS) + { + *total = COSTS_N_INSNS (1) + COSTS_N_INSNS (1) / 2; + return true; + } + + /* Estimate as 4 + 4 * #ofbits. */ + *total = COSTS_N_INSNS (132); + return true; + } + return false; + + case UDIV: + case MOD: + case UMOD: + case DIV: + if (!CONST_INT_P (XEXP (x, 1)) + || exact_log2 (INTVAL (XEXP (x, 1)) < 0)) + { + /* Estimate this as 4 + 8 * #of bits. */ + *total = COSTS_N_INSNS (260); + return true; + } + return false; + + case AND: + if (CONST_INT_P (XEXP (x, 1)) + /* Two constants may actually happen before optimization. */ + && !CONST_INT_P (XEXP (x, 0)) + && !CRIS_CONST_OK_FOR_LETTER_P (INTVAL (XEXP (x, 1)), 'I')) + { + *total = (rtx_cost (XEXP (x, 0), outer_code, speed) + 2 + + 2 * GET_MODE_NUNITS (GET_MODE (XEXP (x, 0)))); + return true; + } + return false; + + case ZERO_EXTEND: case SIGN_EXTEND: + *total = rtx_cost (XEXP (x, 0), outer_code, speed); + return true; + + default: + return false; + } } /* The ADDRESS_COST worker. */ -int -cris_address_cost (x) - rtx x; +static int +cris_address_cost (rtx x, bool speed ATTRIBUTE_UNUSED) { /* The metric to use for the cost-macros is unclear. The metric used here is (the number of cycles needed) / 2, where we consider equal a cycle for a word of code and a cycle to - read memory. */ + read memory. FIXME: Adding "+ 1" to all values would avoid + returning 0, as tree-ssa-loop-ivopts.c as of r128272 "normalizes" + 0 to 1, thereby giving equal costs to [rN + rM] and [rN]. + Unfortunately(?) such a hack would expose other pessimizations, + at least with g++.dg/tree-ssa/ivopts-1.C, adding insns to the + loop there, without apparent reason. */ /* The cheapest addressing modes get 0, since nothing extra is needed. */ if (BASE_OR_AUTOINCR_P (x)) @@ -2091,16 +1885,13 @@ cris_address_cost (x) /* An indirect mem must be a DIP. This means two bytes extra for code, and 4 bytes extra for memory read, i.e. (2 + 4) / 2. */ - if (GET_CODE (x) == MEM) + if (MEM_P (x)) return (2 + 4) / 2; /* Assume (2 + 4) / 2 for a single constant; a dword, since it needs - an extra DIP prefix and 4 bytes of constant in most cases. - For PIC and a symbol with a GOT entry, we double the cost since we - add a [rPIC+...] offset. A GOT-less symbol uses a BDAP prefix - equivalent to the DIP prefix for non-PIC, hence the same cost. */ + an extra DIP prefix and 4 bytes of constant in most cases. */ if (CONSTANT_P (x)) - return flag_pic && cris_got_symbol (x) ? 2 * (2 + 4) / 2 : (2 + 4) / 2; + return (2 + 4) / 2; /* Handle BIAP and BDAP prefixes. */ if (GET_CODE (x) == PLUS) @@ -2108,36 +1899,40 @@ cris_address_cost (x) rtx tem1 = XEXP (x, 0); rtx tem2 = XEXP (x, 1); - /* A BIAP is 2 extra bytes for the prefix insn, nothing more. We - recognize the typical MULT which is always in tem1 because of - insn canonicalization. */ - if ((GET_CODE (tem1) == MULT && BIAP_INDEX_P (tem1)) - || REG_P (tem1)) - return 2 / 2; - - /* A BDAP (quick) is 2 extra bytes. Any constant operand to the - PLUS is always found in tem2. */ - if (GET_CODE (tem2) == CONST_INT - && INTVAL (tem2) < 128 && INTVAL (tem2) >= -128) - return 2 / 2; - - /* A BDAP -32768 .. 32767 is like BDAP quick, but with 2 extra - bytes. */ - if (GET_CODE (tem2) == CONST_INT - && CONST_OK_FOR_LETTER_P (INTVAL (tem2), 'L')) - return (2 + 2) / 2; - - /* A BDAP with some other constant is 2 bytes extra. */ - if (CONSTANT_P (tem2)) + /* Local extended canonicalization rule: the first operand must + be REG, unless it's an operation (MULT). */ + if (!REG_P (tem1) && GET_CODE (tem1) != MULT) + tem1 = tem2, tem2 = XEXP (x, 0); + + /* We'll "assume" we have canonical RTX now. */ + gcc_assert (REG_P (tem1) || GET_CODE (tem1) == MULT); + + /* A BIAP is 2 extra bytes for the prefix insn, nothing more. We + recognize the typical MULT which is always in tem1 because of + insn canonicalization. */ + if ((GET_CODE (tem1) == MULT && BIAP_INDEX_P (tem1)) + || REG_P (tem2)) + return 2 / 2; + + /* A BDAP (quick) is 2 extra bytes. Any constant operand to the + PLUS is always found in tem2. */ + if (CONST_INT_P (tem2) && INTVAL (tem2) < 128 && INTVAL (tem2) >= -128) + return 2 / 2; + + /* A BDAP -32768 .. 32767 is like BDAP quick, but with 2 extra + bytes. */ + if (CONST_INT_P (tem2) + && CRIS_CONST_OK_FOR_LETTER_P (INTVAL (tem2), 'L')) + return (2 + 2) / 2; + + /* A BDAP with some other constant is 2 bytes extra. */ + if (CONSTANT_P (tem2)) + return (2 + 2 + 2) / 2; + + /* BDAP with something indirect should have a higher cost than + BIAP with register. FIXME: Should it cost like a MEM or more? */ return (2 + 2 + 2) / 2; - - /* BDAP with something indirect should have a higher cost than - BIAP with register. FIXME: Should it cost like a MEM or more? */ - /* Don't need to check it, it's the only one left. - FIXME: There was a REG test missing, perhaps there are others. - Think more. */ - return (2 + 2 + 2) / 2; - } + } /* What else? Return a high cost. It matters only for valid addressing modes. */ @@ -2160,10 +1955,9 @@ cris_address_cost (x) whose mode we must consider. */ int -cris_side_effect_mode_ok (code, ops, lreg, rreg, rval, multop, other_op) - enum rtx_code code; - rtx *ops; - int lreg, rreg, rval, multop, other_op; +cris_side_effect_mode_ok (enum rtx_code code, rtx *ops, + int lreg, int rreg, int rval, + int multop, int other_op) { /* Find what value to multiply with, for rx =ry + rz * n. */ int mult = multop < 0 ? 1 : INTVAL (ops[multop]); @@ -2224,21 +2018,19 @@ cris_side_effect_mode_ok (code, ops, lreg, rreg, rval, multop, other_op) /* Do not allow rx = rx + n if a normal add or sub with same size would do. */ if (rtx_equal_p (ops[lreg], reg_rtx) - && GET_CODE (val_rtx) == CONST_INT + && CONST_INT_P (val_rtx) && (INTVAL (val_rtx) <= 63 && INTVAL (val_rtx) >= -63)) return 0; - /* Check allowed cases, like [r(+)?].[bwd] and const. - A symbol is not allowed with PIC. */ + /* Check allowed cases, like [r(+)?].[bwd] and const. */ if (CONSTANT_P (val_rtx)) - return flag_pic == 0 || cris_symbol (val_rtx) == 0; + return 1; - if (GET_CODE (val_rtx) == MEM - && BASE_OR_AUTOINCR_P (XEXP (val_rtx, 0))) + if (MEM_P (val_rtx) && BASE_OR_AUTOINCR_P (XEXP (val_rtx, 0))) return 1; if (GET_CODE (val_rtx) == SIGN_EXTEND - && GET_CODE (XEXP (val_rtx, 0)) == MEM + && MEM_P (XEXP (val_rtx, 0)) && BASE_OR_AUTOINCR_P (XEXP (XEXP (val_rtx, 0), 0))) return 1; @@ -2270,6 +2062,49 @@ cris_side_effect_mode_ok (code, ops, lreg, rreg, rval, multop, other_op) internal_error ("internal error: cris_side_effect_mode_ok with bad operands"); } +/* Whether next_cc0_user of insn is LE or GT or requires a real compare + insn for other reasons. */ + +bool +cris_cc0_user_requires_cmp (rtx insn) +{ + rtx cc0_user = NULL; + rtx body; + rtx set; + + gcc_assert (insn != NULL); + + if (!TARGET_V32) + return false; + + cc0_user = next_cc0_user (insn); + if (cc0_user == NULL) + return false; + + body = PATTERN (cc0_user); + set = single_set (cc0_user); + + /* Users can be sCC and bCC. */ + if (JUMP_P (cc0_user) + && GET_CODE (body) == SET + && SET_DEST (body) == pc_rtx + && GET_CODE (SET_SRC (body)) == IF_THEN_ELSE + && XEXP (XEXP (SET_SRC (body), 0), 0) == cc0_rtx) + { + return + GET_CODE (XEXP (SET_SRC (body), 0)) == GT + || GET_CODE (XEXP (SET_SRC (body), 0)) == LE; + } + else if (set) + { + return + GET_CODE (SET_SRC (body)) == GT + || GET_CODE (SET_SRC (body)) == LE; + } + + gcc_unreachable (); +} + /* The function reg_overlap_mentioned_p in CVS (still as of 2001-05-16) does not handle the case where the IN operand is strict_low_part; it does handle it for X. Test-case in Axis-20010516. This function takes @@ -2277,8 +2112,7 @@ cris_side_effect_mode_ok (code, ops, lreg, rreg, rval, multop, other_op) anyway. */ static int -cris_reg_overlap_mentioned_p (x, in) - rtx x, in; +cris_reg_overlap_mentioned_p (rtx x, rtx in) { /* The function reg_overlap_mentioned now handles when X is strict_low_part, but not when IN is a STRICT_LOW_PART. */ @@ -2292,174 +2126,224 @@ cris_reg_overlap_mentioned_p (x, in) We just dispatch to the functions for ELF and a.out. */ void -cris_target_asm_named_section (name, flags) - const char *name; - unsigned int flags; +cris_target_asm_named_section (const char *name, unsigned int flags, + tree decl) { if (! TARGET_ELF) - default_no_named_section (name, flags); + default_no_named_section (name, flags, decl); else - default_elf_asm_named_section (name, flags); + default_elf_asm_named_section (name, flags, decl); } -/* The LEGITIMATE_PIC_OPERAND_P worker. */ +/* Return TRUE iff X is a CONST valid for e.g. indexing. + ANY_OPERAND is 0 if X is in a CALL_P insn or movsi, 1 + elsewhere. */ -int -cris_legitimate_pic_operand (x) - rtx x; +bool +cris_valid_pic_const (rtx x, bool any_operand) { - /* The PIC representation of a symbol with a GOT entry will be (for - example; relocations differ): - sym => [rPIC+sym:GOT] - and for a GOT-less symbol it will be (for example, relocation differ): - sym => rPIC+sym:GOTOFF - so only a symbol with a GOT is by itself a valid operand, and it - can't be a sum of a symbol and an offset. */ - return ! cris_symbol (x) || cris_got_symbol (x); -} - -/* Return non-zero if there's a SYMBOL_REF or LABEL_REF hiding inside this - CONSTANT_P. */ + gcc_assert (flag_pic); -int -cris_symbol (x) - rtx x; -{ switch (GET_CODE (x)) { - case SYMBOL_REF: - case LABEL_REF: - return 1; - - case UNSPEC: - /* A PLT reference. */ - ASSERT_PLT_UNSPEC (x); - return 1; - - case CONST: - return cris_symbol (XEXP (x, 0)); - - case PLUS: - case MINUS: - return cris_symbol (XEXP (x, 0)) || cris_symbol (XEXP (x, 1)); - case CONST_INT: case CONST_DOUBLE: - case CONSTANT_P_RTX: - return 0; - + return true; default: - fatal_insn ("unrecognized supposed constant", x); + ; } - return 1; + if (GET_CODE (x) != CONST) + return false; + + x = XEXP (x, 0); + + /* Handle (const (plus (unspec .. UNSPEC_GOTREL) (const_int ...))). */ + if (GET_CODE (x) == PLUS + && GET_CODE (XEXP (x, 0)) == UNSPEC + && (XINT (XEXP (x, 0), 1) == CRIS_UNSPEC_GOTREL + || XINT (XEXP (x, 0), 1) == CRIS_UNSPEC_PCREL) + && CONST_INT_P (XEXP (x, 1))) + x = XEXP (x, 0); + + if (GET_CODE (x) == UNSPEC) + switch (XINT (x, 1)) + { + /* A PCREL operand is only valid for call and movsi. */ + case CRIS_UNSPEC_PLT_PCREL: + case CRIS_UNSPEC_PCREL: + return !any_operand; + + case CRIS_UNSPEC_PLT_GOTREL: + case CRIS_UNSPEC_PLTGOTREAD: + case CRIS_UNSPEC_GOTREAD: + case CRIS_UNSPEC_GOTREL: + return true; + default: + gcc_unreachable (); + } + + return cris_pic_symbol_type_of (x) == cris_no_symbol; } -/* Return non-zero if there's a SYMBOL_REF or LABEL_REF hiding inside this - CONSTANT_P, and the symbol does not need a GOT entry. Also set - current_function_uses_pic_offset_table if we're generating PIC and ever - see something that would need one. */ +/* Helper function to find the right PIC-type symbol to generate, + given the original (non-PIC) representation. */ -int -cris_gotless_symbol (x) - rtx x; +enum cris_pic_symbol_type +cris_pic_symbol_type_of (rtx x) { switch (GET_CODE (x)) { - case UNSPEC: - ASSERT_PLT_UNSPEC (x); - return 1; - case SYMBOL_REF: - if (flag_pic && cfun != NULL) - current_function_uses_pic_offset_table = 1; - return SYMBOL_REF_FLAG (x); + return SYMBOL_REF_LOCAL_P (x) + ? cris_rel_symbol : cris_got_symbol; case LABEL_REF: - /* We don't set current_function_uses_pic_offset_table for - LABEL_REF:s in here, since they are almost always originating - from some branch. The only time it does not come from a label is - when GCC does something like __builtin_setjmp. Then we get the - LABEL_REF from the movsi expander, so we mark it there as a - special case. */ - return 1; + return cris_rel_symbol; case CONST: - return cris_gotless_symbol (XEXP (x, 0)); + return cris_pic_symbol_type_of (XEXP (x, 0)); case PLUS: case MINUS: { - int x0 = cris_gotless_symbol (XEXP (x, 0)) != 0; - int x1 = cris_gotless_symbol (XEXP (x, 1)) != 0; - - /* One and only one of them must be a local symbol. Neither must - be some other, more general kind of symbol. */ - return - (x0 ^ x1) - && ! (x0 == 0 && cris_symbol (XEXP (x, 0))) - && ! (x1 == 0 && cris_symbol (XEXP (x, 1))); + enum cris_pic_symbol_type t1 = cris_pic_symbol_type_of (XEXP (x, 0)); + enum cris_pic_symbol_type t2 = cris_pic_symbol_type_of (XEXP (x, 1)); + + gcc_assert (t1 == cris_no_symbol || t2 == cris_no_symbol); + + if (t1 == cris_got_symbol || t1 == cris_got_symbol) + return cris_got_symbol_needing_fixup; + + return t1 != cris_no_symbol ? t1 : t2; } case CONST_INT: case CONST_DOUBLE: - case CONSTANT_P_RTX: - return 0; + return cris_no_symbol; + + case UNSPEC: + /* Likely an offsettability-test attempting to add a constant to + a GOTREAD symbol, which can't be handled. */ + return cris_invalid_pic_symbol; default: fatal_insn ("unrecognized supposed constant", x); } - return 1; + gcc_unreachable (); } -/* Return non-zero if there's a SYMBOL_REF or LABEL_REF hiding inside this - CONSTANT_P, and the symbol needs a GOT entry. */ +/* The LEGITIMATE_PIC_OPERAND_P worker. */ int -cris_got_symbol (x) - rtx x; +cris_legitimate_pic_operand (rtx x) { - switch (GET_CODE (x)) + /* Symbols are not valid PIC operands as-is; just constants. */ + return cris_valid_pic_const (x, true); +} + +/* The ASM_OUTPUT_CASE_END worker. */ + +void +cris_asm_output_case_end (FILE *stream, int num, rtx table) +{ + if (TARGET_V32) { - case UNSPEC: - ASSERT_PLT_UNSPEC (x); - return 0; + rtx whole_jump_insn = PATTERN (PREV_INSN (PREV_INSN (table))); + + /* This can be a SEQUENCE, meaning the delay-slot of the jump is + filled. */ + rtx parallel_jump + = (GET_CODE (whole_jump_insn) == SEQUENCE + ? PATTERN (XVECEXP (whole_jump_insn, 0, 0)) : whole_jump_insn); + + asm_fprintf (stream, + "\t.word %LL%d-.%s\n", + CODE_LABEL_NUMBER (XEXP (XEXP (XEXP (XVECEXP + (parallel_jump, 0, 0), + 1), 2), 0)), + (TARGET_PDEBUG ? "; default" : "")); + return; + } - case SYMBOL_REF: - if (flag_pic && cfun != NULL) - current_function_uses_pic_offset_table = 1; - return ! SYMBOL_REF_FLAG (x); + asm_fprintf (stream, + "\t.word %LL%d-%LL%d%s\n", + CODE_LABEL_NUMBER (XEXP + (XEXP + (XEXP + (XVECEXP + (PATTERN + (PREV_INSN + (PREV_INSN (table))), 0, 0), 1), + 2), 0)), + num, + (TARGET_PDEBUG ? "; default" : "")); +} - case CONST: - return cris_got_symbol (XEXP (x, 0)); +/* TARGET_HANDLE_OPTION worker. We just store the values into local + variables here. Checks for correct semantics are in + cris_override_options. */ - case LABEL_REF: - /* A LABEL_REF is never visible as a symbol outside the local - function. */ - case PLUS: - case MINUS: - /* Nope, can't access the GOT for "symbol + offset". */ - return 0; +static bool +cris_handle_option (size_t code, const char *arg ATTRIBUTE_UNUSED, + int value ATTRIBUTE_UNUSED) +{ + switch (code) + { + case OPT_metrax100: + target_flags + |= (MASK_SVINTO + + MASK_ETRAX4_ADD + + MASK_ALIGN_BY_32); + break; - case CONST_INT: - case CONST_DOUBLE: - case CONSTANT_P_RTX: - return 0; + case OPT_mno_etrax100: + target_flags + &= ~(MASK_SVINTO + + MASK_ETRAX4_ADD + + MASK_ALIGN_BY_32); + break; + + case OPT_m32_bit: + case OPT_m32bit: + target_flags + |= (MASK_STACK_ALIGN + + MASK_CONST_ALIGN + + MASK_DATA_ALIGN + + MASK_ALIGN_BY_32); + break; + + case OPT_m16_bit: + case OPT_m16bit: + target_flags + |= (MASK_STACK_ALIGN + + MASK_CONST_ALIGN + + MASK_DATA_ALIGN); + break; + + case OPT_m8_bit: + case OPT_m8bit: + target_flags + &= ~(MASK_STACK_ALIGN + + MASK_CONST_ALIGN + + MASK_DATA_ALIGN); + break; default: - fatal_insn ("unrecognized supposed constant in cris_global_pic_symbol", - x); + break; } - return 1; + CRIS_SUBTARGET_HANDLE_OPTION(code, arg, value); + + return true; } /* The OVERRIDE_OPTIONS worker. As is the norm, this also parses -mfoo=bar type parameters. */ void -cris_override_options () +cris_override_options (void) { if (cris_max_stackframe_str) { @@ -2494,20 +2378,20 @@ cris_override_options () || strcmp ("etrax100lx", cris_cpu_str) == 0) cris_cpu_version = 10; - if (cris_cpu_version < 0 || cris_cpu_version > 10) + if (cris_cpu_version < 0 || cris_cpu_version > 32) error ("unknown CRIS version specification in -march= or -mcpu= : %s", cris_cpu_str); /* Set the target flags. */ if (cris_cpu_version >= CRIS_CPU_ETRAX4) - target_flags |= TARGET_MASK_ETRAX4_ADD; + target_flags |= MASK_ETRAX4_ADD; /* If this is Svinto or higher, align for 32 bit accesses. */ if (cris_cpu_version >= CRIS_CPU_SVINTO) target_flags - |= (TARGET_MASK_SVINTO | TARGET_MASK_ALIGN_BY_32 - | TARGET_MASK_STACK_ALIGN | TARGET_MASK_CONST_ALIGN - | TARGET_MASK_DATA_ALIGN); + |= (MASK_SVINTO | MASK_ALIGN_BY_32 + | MASK_STACK_ALIGN | MASK_CONST_ALIGN + | MASK_DATA_ALIGN); /* Note that we do not add new flags when it can be completely described with a macro that uses -mcpu=X. So @@ -2530,7 +2414,7 @@ cris_override_options () || strcmp ("etrax100lx", cris_tune_str) == 0) cris_tune = 10; - if (cris_tune < 0 || cris_tune > 10) + if (cris_tune < 0 || cris_tune > 32) error ("unknown CRIS cpu version specification in -mtune= : %s", cris_tune_str); @@ -2538,10 +2422,13 @@ cris_override_options () /* We have currently nothing more to tune than alignment for memory accesses. */ target_flags - |= (TARGET_MASK_STACK_ALIGN | TARGET_MASK_CONST_ALIGN - | TARGET_MASK_DATA_ALIGN | TARGET_MASK_ALIGN_BY_32); + |= (MASK_STACK_ALIGN | MASK_CONST_ALIGN + | MASK_DATA_ALIGN | MASK_ALIGN_BY_32); } + if (cris_cpu_version >= CRIS_CPU_V32) + target_flags &= ~(MASK_SIDE_EFFECT_PREFIXES|MASK_MUL_BUG); + if (flag_pic) { /* Use error rather than warning, so invalid use is easily @@ -2562,10 +2449,9 @@ cris_override_options () flag_no_function_cse = 1; } - if ((write_symbols == DWARF_DEBUG - || write_symbols == DWARF2_DEBUG) && ! TARGET_ELF) + if (write_symbols == DWARF2_DEBUG && ! TARGET_ELF) { - warning ("that particular -g option is invalid with -maout and -melinux"); + warning (0, "that particular -g option is invalid with -maout and -melinux"); write_symbols = DBX_DEBUG; } @@ -2573,147 +2459,105 @@ cris_override_options () init_machine_status = cris_init_machine_status; } -/* The ASM_OUTPUT_MI_THUNK worker. */ +/* The TARGET_ASM_OUTPUT_MI_THUNK worker. */ -void -cris_asm_output_mi_thunk (stream, thunkdecl, delta, funcdecl) - FILE *stream; - tree thunkdecl ATTRIBUTE_UNUSED; - int delta; - tree funcdecl; +static void +cris_asm_output_mi_thunk (FILE *stream, + tree thunkdecl ATTRIBUTE_UNUSED, + HOST_WIDE_INT delta, + HOST_WIDE_INT vcall_offset ATTRIBUTE_UNUSED, + tree funcdecl) { if (delta > 0) - asm_fprintf (stream, "\tadd%s %d,$%s\n", - ADDITIVE_SIZE_MODIFIER (delta), delta, - reg_names[CRIS_FIRST_ARG_REG]); + fprintf (stream, "\tadd%s " HOST_WIDE_INT_PRINT_DEC ",$%s\n", + ADDITIVE_SIZE_MODIFIER (delta), delta, + reg_names[CRIS_FIRST_ARG_REG]); else if (delta < 0) - asm_fprintf (stream, "\tsub%s %d,$%s\n", - ADDITIVE_SIZE_MODIFIER (-delta), -delta, - reg_names[CRIS_FIRST_ARG_REG]); + fprintf (stream, "\tsub%s " HOST_WIDE_INT_PRINT_DEC ",$%s\n", + ADDITIVE_SIZE_MODIFIER (-delta), -delta, + reg_names[CRIS_FIRST_ARG_REG]); if (flag_pic) { const char *name = XSTR (XEXP (DECL_RTL (funcdecl), 0), 0); - STRIP_NAME_ENCODING (name, name); - fprintf (stream, "add.d "); - assemble_name (stream, name); - fprintf (stream, "%s,$pc\n", CRIS_PLT_PCOFFSET_SUFFIX); + name = (* targetm.strip_name_encoding) (name); + + if (TARGET_V32) + { + fprintf (stream, "\tba "); + assemble_name (stream, name); + fprintf (stream, "%s\n", CRIS_PLT_PCOFFSET_SUFFIX); + } + else + { + fprintf (stream, "add.d "); + assemble_name (stream, name); + fprintf (stream, "%s,$pc\n", CRIS_PLT_PCOFFSET_SUFFIX); + } } else { fprintf (stream, "jump "); assemble_name (stream, XSTR (XEXP (DECL_RTL (funcdecl), 0), 0)); fprintf (stream, "\n"); + + if (TARGET_V32) + fprintf (stream, "\tnop\n"); } } -/* The EXPAND_BUILTIN_VA_ARG worker. This is modified from the - "standard" implementation of va_arg: read the value from the current - address and increment by the size of one or two registers. The - important difference for CRIS is that if the type is - pass-by-reference, then perform an indirection. */ +/* Boilerplate emitted at start of file. -rtx -cris_expand_builtin_va_arg (valist, type) - tree valist; - tree type; -{ - tree addr_tree, t; - rtx addr; - tree passed_size = size_zero_node; - tree type_size = NULL; - tree size3 = size_int (3); - tree size4 = size_int (4); - tree size8 = size_int (8); - tree rounded_size; - - /* Get AP. */ - addr_tree = valist; - - if (type == error_mark_node - || (type_size = TYPE_SIZE_UNIT (TYPE_MAIN_VARIANT (type))) == NULL - || TREE_OVERFLOW (type_size)) - /* Presumably an error; the size isn't computable. A message has - supposedly been emitted elsewhere. */ - rounded_size = size_zero_node; - else - rounded_size - = fold (build (MULT_EXPR, sizetype, - fold (build (TRUNC_DIV_EXPR, sizetype, - fold (build (PLUS_EXPR, sizetype, - type_size, size3)), - size4)), - size4)); - - if (!integer_zerop (rounded_size)) - { - /* Check if the type is passed by value or by reference. Values up - to 8 bytes are passed by-value, padded to register-size (4 - bytes). Larger values and varying-size types are passed - by reference. */ - passed_size - = (!really_constant_p (type_size) - ? size4 - : fold (build (COND_EXPR, sizetype, - fold (build (GT_EXPR, sizetype, - rounded_size, - size8)), - size4, - rounded_size))); - - addr_tree - = (!really_constant_p (type_size) - ? build1 (INDIRECT_REF, build_pointer_type (type), addr_tree) - : fold (build (COND_EXPR, TREE_TYPE (addr_tree), - fold (build (GT_EXPR, sizetype, - rounded_size, - size8)), - build1 (INDIRECT_REF, build_pointer_type (type), - addr_tree), - addr_tree))); - } - - addr = expand_expr (addr_tree, NULL_RTX, Pmode, EXPAND_NORMAL); - addr = copy_to_reg (addr); - - if (!integer_zerop (rounded_size)) - { - /* Compute new value for AP. */ - t = build (MODIFY_EXPR, TREE_TYPE (valist), valist, - build (PLUS_EXPR, TREE_TYPE (valist), valist, - passed_size)); - TREE_SIDE_EFFECTS (t) = 1; - expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL); - } - - return addr; + NO_APP *only at file start* means faster assembly. It also means + comments are not allowed. In some cases comments will be output + for debugging purposes. Make sure they are allowed then. + + We want a .file directive only if TARGET_ELF. */ +static void +cris_file_start (void) +{ + /* These expressions can vary at run time, so we cannot put + them into TARGET_INITIALIZER. */ + targetm.file_start_app_off = !(TARGET_PDEBUG || flag_print_asm_name); + targetm.file_start_file_directive = TARGET_ELF; + + default_file_start (); +} + +/* Rename the function calls for integer multiply and divide. */ +static void +cris_init_libfuncs (void) +{ + set_optab_libfunc (smul_optab, SImode, "__Mul"); + set_optab_libfunc (sdiv_optab, SImode, "__Div"); + set_optab_libfunc (udiv_optab, SImode, "__Udiv"); + set_optab_libfunc (smod_optab, SImode, "__Mod"); + set_optab_libfunc (umod_optab, SImode, "__Umod"); } /* The INIT_EXPANDERS worker sets the per-function-data initializer and mark functions. */ void -cris_init_expanders () +cris_init_expanders (void) { /* Nothing here at the moment. */ } /* Zero initialization is OK for all current fields. */ -static void -cris_init_machine_status (p) - struct function *p; +static struct machine_function * +cris_init_machine_status (void) { - p->machine = xcalloc (1, sizeof (struct machine_function)); + return GGC_CNEW (struct machine_function); } /* Split a 2 word move (DI or presumably DF) into component parts. Originally a copy of gen_split_move_double in m32r.c. */ rtx -cris_split_movdx (operands) - rtx *operands; +cris_split_movdx (rtx *operands) { enum machine_mode mode = GET_MODE (operands[0]); rtx dest = operands[0]; @@ -2723,16 +2567,15 @@ cris_split_movdx (operands) /* We used to have to handle (SUBREG (MEM)) here, but that should no longer happen; after reload there are no SUBREGs any more, and we're only called after reload. */ - if (GET_CODE (dest) == SUBREG || GET_CODE (src) == SUBREG) - abort (); + CRIS_ASSERT (GET_CODE (dest) != SUBREG && GET_CODE (src) != SUBREG); start_sequence (); - if (GET_CODE (dest) == REG) + if (REG_P (dest)) { int dregno = REGNO (dest); /* Reg-to-reg copy. */ - if (GET_CODE (src) == REG) + if (REG_P (src)) { int sregno = REGNO (src); @@ -2750,7 +2593,7 @@ cris_split_movdx (operands) operand_subword (src, !reverse, TRUE, mode))); } /* Constant-to-reg copy. */ - else if (GET_CODE (src) == CONST_INT || GET_CODE (src) == CONST_DOUBLE) + else if (CONST_INT_P (src) || GET_CODE (src) == CONST_DOUBLE) { rtx words[2]; split_double (src, &words[0], &words[1]); @@ -2763,7 +2606,7 @@ cris_split_movdx (operands) words[1])); } /* Mem-to-reg copy. */ - else if (GET_CODE (src) == MEM) + else if (MEM_P (src)) { /* If the high-address word is used in the address, we must load it last. Otherwise, load it first. */ @@ -2771,7 +2614,7 @@ cris_split_movdx (operands) int reverse = (refers_to_regno_p (dregno, dregno + 1, addr, NULL) != 0); - /* The original code imples that we can't do + /* The original code implies that we can't do move.x [rN+],rM move.x [rN],rM+1 when rN is dead, because of REG_NOTES damage. That is consistent with what I've seen, so don't try it. @@ -2781,12 +2624,31 @@ cris_split_movdx (operands) if (GET_CODE (addr) == POST_INC) { - emit_insn (gen_rtx_SET (VOIDmode, - operand_subword (dest, 0, TRUE, mode), - change_address (src, SImode, addr))); - emit_insn (gen_rtx_SET (VOIDmode, - operand_subword (dest, 1, TRUE, mode), - change_address (src, SImode, addr))); + rtx mem; + rtx insn; + + /* Whenever we emit insns with post-incremented + addresses ourselves, we must add a post-inc note + manually. */ + mem = change_address (src, SImode, addr); + insn + = gen_rtx_SET (VOIDmode, + operand_subword (dest, 0, TRUE, mode), mem); + insn = emit_insn (insn); + if (GET_CODE (XEXP (mem, 0)) == POST_INC) + REG_NOTES (insn) + = alloc_EXPR_LIST (REG_INC, XEXP (XEXP (mem, 0), 0), + REG_NOTES (insn)); + + mem = copy_rtx (mem); + insn + = gen_rtx_SET (VOIDmode, + operand_subword (dest, 1, TRUE, mode), mem); + insn = emit_insn (insn); + if (GET_CODE (XEXP (mem, 0)) == POST_INC) + REG_NOTES (insn) + = alloc_EXPR_LIST (REG_INC, XEXP (XEXP (mem, 0), 0), + REG_NOTES (insn)); } else { @@ -2815,11 +2677,11 @@ cris_split_movdx (operands) } } else - abort (); + internal_error ("Unknown src"); } /* Reg-to-mem copy or clear mem. */ - else if (GET_CODE (dest) == MEM - && (GET_CODE (src) == REG + else if (MEM_P (dest) + && (REG_P (src) || src == const0_rtx || src == CONST0_RTX (DFmode))) { @@ -2827,12 +2689,31 @@ cris_split_movdx (operands) if (GET_CODE (addr) == POST_INC) { - emit_insn (gen_rtx_SET (VOIDmode, - change_address (dest, SImode, addr), - operand_subword (src, 0, TRUE, mode))); - emit_insn (gen_rtx_SET (VOIDmode, - change_address (dest, SImode, addr), - operand_subword (src, 1, TRUE, mode))); + rtx mem; + rtx insn; + + /* Whenever we emit insns with post-incremented addresses + ourselves, we must add a post-inc note manually. */ + mem = change_address (dest, SImode, addr); + insn + = gen_rtx_SET (VOIDmode, + mem, operand_subword (src, 0, TRUE, mode)); + insn = emit_insn (insn); + if (GET_CODE (XEXP (mem, 0)) == POST_INC) + REG_NOTES (insn) + = alloc_EXPR_LIST (REG_INC, XEXP (XEXP (mem, 0), 0), + REG_NOTES (insn)); + + mem = copy_rtx (mem); + insn + = gen_rtx_SET (VOIDmode, + mem, + operand_subword (src, 1, TRUE, mode)); + insn = emit_insn (insn); + if (GET_CODE (XEXP (mem, 0)) == POST_INC) + REG_NOTES (insn) + = alloc_EXPR_LIST (REG_INC, XEXP (XEXP (mem, 0), 0), + REG_NOTES (insn)); } else { @@ -2857,214 +2738,1070 @@ cris_split_movdx (operands) } else - abort (); + internal_error ("Unknown dest"); - val = gen_sequence (); + val = get_insns (); end_sequence (); return val; } -/* This is in essence a copy of output_addr_const altered to output - symbolic operands as PIC. - - FIXME: Add hooks similar to ASM_OUTPUT_SYMBOL_REF to get this effect in - the "real" output_addr_const. All we need is one for LABEL_REF (and - one for CODE_LABEL?). */ +/* The expander for the prologue pattern name. */ void -cris_output_addr_const (file, x) - FILE *file; - rtx x; +cris_expand_prologue (void) { - int is_plt = 0; + int regno; + int size = get_frame_size (); + /* Shorten the used name for readability. */ + int cfoa_size = crtl->outgoing_args_size; + int last_movem_reg = -1; + int framesize = 0; + rtx mem, insn; + int return_address_on_stack = cris_return_address_on_stack (); + int got_really_used = false; + int n_movem_regs = 0; + int pretend = crtl->args.pretend_args_size; -restart: - switch (GET_CODE (x)) + /* Don't do anything if no prologues or epilogues are wanted. */ + if (!TARGET_PROLOGUE_EPILOGUE) + return; + + CRIS_ASSERT (size >= 0); + + if (crtl->uses_pic_offset_table) { - case UNSPEC: - ASSERT_PLT_UNSPEC (x); - x = XVECEXP (x, 0, 0); - is_plt = 1; + /* A reference may have been optimized out (like the abort () in + fde_split in unwind-dw2-fde.c, at least 3.2.1) so check that + it's still used. */ + push_topmost_sequence (); + got_really_used + = reg_used_between_p (pic_offset_table_rtx, get_insns (), NULL_RTX); + pop_topmost_sequence (); + } - /* Fall through. */ - case SYMBOL_REF: - if (flag_pic) + /* Align the size to what's best for the CPU model. */ + if (TARGET_STACK_ALIGN) + size = TARGET_ALIGN_BY_32 ? (size + 3) & ~3 : (size + 1) & ~1; + + if (pretend) + { + /* See also cris_setup_incoming_varargs where + cfun->machine->stdarg_regs is set. There are other setters of + crtl->args.pretend_args_size than stdarg handling, like + for an argument passed with parts in R13 and stack. We must + not store R13 into the pretend-area for that case, as GCC does + that itself. "Our" store would be marked as redundant and GCC + will attempt to remove it, which will then be flagged as an + internal error; trying to remove a frame-related insn. */ + int stdarg_regs = cfun->machine->stdarg_regs; + + framesize += pretend; + + for (regno = CRIS_FIRST_ARG_REG + CRIS_MAX_ARGS_IN_REGS - 1; + stdarg_regs > 0; + regno--, pretend -= 4, stdarg_regs--) { - const char *origstr = XSTR (x, 0); - const char *str; + insn = emit_insn (gen_rtx_SET (VOIDmode, + stack_pointer_rtx, + plus_constant (stack_pointer_rtx, + -4))); + /* FIXME: When dwarf2 frame output and unless asynchronous + exceptions, make dwarf2 bundle together all stack + adjustments like it does for registers between stack + adjustments. */ + RTX_FRAME_RELATED_P (insn) = 1; + + mem = gen_rtx_MEM (SImode, stack_pointer_rtx); + set_mem_alias_set (mem, get_varargs_alias_set ()); + insn = emit_move_insn (mem, gen_rtx_raw_REG (SImode, regno)); + + /* Note the absence of RTX_FRAME_RELATED_P on the above insn: + the value isn't restored, so we don't want to tell dwarf2 + that it's been stored to stack, else EH handling info would + get confused. */ + } + + /* For other setters of crtl->args.pretend_args_size, we + just adjust the stack by leaving the remaining size in + "pretend", handled below. */ + } + + /* Save SRP if not a leaf function. */ + if (return_address_on_stack) + { + insn = emit_insn (gen_rtx_SET (VOIDmode, + stack_pointer_rtx, + plus_constant (stack_pointer_rtx, + -4 - pretend))); + pretend = 0; + RTX_FRAME_RELATED_P (insn) = 1; + + mem = gen_rtx_MEM (SImode, stack_pointer_rtx); + set_mem_alias_set (mem, get_frame_alias_set ()); + insn = emit_move_insn (mem, gen_rtx_raw_REG (SImode, CRIS_SRP_REGNUM)); + RTX_FRAME_RELATED_P (insn) = 1; + framesize += 4; + } + + /* Set up the frame pointer, if needed. */ + if (frame_pointer_needed) + { + insn = emit_insn (gen_rtx_SET (VOIDmode, + stack_pointer_rtx, + plus_constant (stack_pointer_rtx, + -4 - pretend))); + pretend = 0; + RTX_FRAME_RELATED_P (insn) = 1; + + mem = gen_rtx_MEM (SImode, stack_pointer_rtx); + set_mem_alias_set (mem, get_frame_alias_set ()); + insn = emit_move_insn (mem, frame_pointer_rtx); + RTX_FRAME_RELATED_P (insn) = 1; - STRIP_NAME_ENCODING (str, origstr); + insn = emit_move_insn (frame_pointer_rtx, stack_pointer_rtx); + RTX_FRAME_RELATED_P (insn) = 1; - if (is_plt) + framesize += 4; + } + + /* Between frame-pointer and saved registers lie the area for local + variables. If we get here with "pretended" size remaining, count + it into the general stack size. */ + size += pretend; + + /* Get a contiguous sequence of registers, starting with R0, that need + to be saved. */ + for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) + { + if (cris_reg_saved_in_regsave_area (regno, got_really_used)) + { + n_movem_regs++; + + /* Check if movem may be used for registers so far. */ + if (regno == last_movem_reg + 1) + /* Yes, update next expected register. */ + last_movem_reg = regno; + else { - if (cris_pic_sympart_only) - { - assemble_name (file, str); - fprintf (file, ":PLTG"); - } - else + /* We cannot use movem for all registers. We have to flush + any movem:ed registers we got so far. */ + if (last_movem_reg != -1) { - if (TARGET_AVOID_GOTPLT) - /* We shouldn't get here. */ - abort (); - - fprintf (file, "[$%s+", reg_names [PIC_OFFSET_TABLE_REGNUM]); - assemble_name (file, XSTR (x, 0)); + int n_saved + = (n_movem_regs == 1) ? 1 : last_movem_reg + 1; - if (flag_pic == 1) - fprintf (file, ":GOTPLT16]"); + /* It is a win to use a side-effect assignment for + 64 <= size <= 128. But side-effect on movem was + not usable for CRIS v0..3. Also only do it if + side-effects insns are allowed. */ + if ((last_movem_reg + 1) * 4 + size >= 64 + && (last_movem_reg + 1) * 4 + size <= 128 + && (cris_cpu_version >= CRIS_CPU_SVINTO || n_saved == 1) + && TARGET_SIDE_EFFECT_PREFIXES) + { + mem + = gen_rtx_MEM (SImode, + plus_constant (stack_pointer_rtx, + -(n_saved * 4 + size))); + set_mem_alias_set (mem, get_frame_alias_set ()); + insn + = cris_emit_movem_store (mem, GEN_INT (n_saved), + -(n_saved * 4 + size), + true); + } else - fprintf (file, ":GOTPLT]"); + { + insn + = gen_rtx_SET (VOIDmode, + stack_pointer_rtx, + plus_constant (stack_pointer_rtx, + -(n_saved * 4 + size))); + insn = emit_insn (insn); + RTX_FRAME_RELATED_P (insn) = 1; + + mem = gen_rtx_MEM (SImode, stack_pointer_rtx); + set_mem_alias_set (mem, get_frame_alias_set ()); + insn = cris_emit_movem_store (mem, GEN_INT (n_saved), + 0, true); + } + + framesize += n_saved * 4 + size; + last_movem_reg = -1; + size = 0; } - } - else if (cris_gotless_symbol (x)) - { - if (! cris_pic_sympart_only) - fprintf (file, "$%s+", reg_names [PIC_OFFSET_TABLE_REGNUM]); - assemble_name (file, str); - fprintf (file, ":GOTOFF"); - } - else if (cris_got_symbol (x)) - { - if (cris_pic_sympart_only) - abort (); - fprintf (file, "[$%s+", reg_names [PIC_OFFSET_TABLE_REGNUM]); - assemble_name (file, XSTR (x, 0)); - if (flag_pic == 1) - fprintf (file, ":GOT16]"); - else - fprintf (file, ":GOT]"); + insn = emit_insn (gen_rtx_SET (VOIDmode, + stack_pointer_rtx, + plus_constant (stack_pointer_rtx, + -4 - size))); + RTX_FRAME_RELATED_P (insn) = 1; + + mem = gen_rtx_MEM (SImode, stack_pointer_rtx); + set_mem_alias_set (mem, get_frame_alias_set ()); + insn = emit_move_insn (mem, gen_rtx_raw_REG (SImode, regno)); + RTX_FRAME_RELATED_P (insn) = 1; + + framesize += 4 + size; + size = 0; } - else - LOSE_AND_RETURN ("unexpected PIC symbol", x); + } + } + + /* Check after, if we could movem all registers. This is the normal case. */ + if (last_movem_reg != -1) + { + int n_saved + = (n_movem_regs == 1) ? 1 : last_movem_reg + 1; - /* Sanity check. */ - if (! current_function_uses_pic_offset_table) - output_operand_lossage ("PIC register isn't set up"); + /* Side-effect on movem was not usable for CRIS v0..3. Also only + do it if side-effects insns are allowed. */ + if ((last_movem_reg + 1) * 4 + size >= 64 + && (last_movem_reg + 1) * 4 + size <= 128 + && (cris_cpu_version >= CRIS_CPU_SVINTO || n_saved == 1) + && TARGET_SIDE_EFFECT_PREFIXES) + { + mem + = gen_rtx_MEM (SImode, + plus_constant (stack_pointer_rtx, + -(n_saved * 4 + size))); + set_mem_alias_set (mem, get_frame_alias_set ()); + insn = cris_emit_movem_store (mem, GEN_INT (n_saved), + -(n_saved * 4 + size), true); } else - assemble_name (file, XSTR (x, 0)); - break; + { + insn + = gen_rtx_SET (VOIDmode, + stack_pointer_rtx, + plus_constant (stack_pointer_rtx, + -(n_saved * 4 + size))); + insn = emit_insn (insn); + RTX_FRAME_RELATED_P (insn) = 1; + + mem = gen_rtx_MEM (SImode, stack_pointer_rtx); + set_mem_alias_set (mem, get_frame_alias_set ()); + insn = cris_emit_movem_store (mem, GEN_INT (n_saved), 0, true); + } - case LABEL_REF: - /* If we get one of those here, it should be dressed as PIC. Branch - labels are normally output with the 'l' specifier, which means it - will go directly to output_asm_label and not end up here. */ - if (GET_CODE (XEXP (x, 0)) != CODE_LABEL - && (GET_CODE (XEXP (x, 0)) != NOTE - || NOTE_LINE_NUMBER (XEXP (x, 0)) != NOTE_INSN_DELETED_LABEL)) - fatal_insn ("unexpected address expression", x); - - if (flag_pic) + framesize += n_saved * 4 + size; + /* We have to put outgoing argument space after regs. */ + if (cfoa_size) { - if (cris_gotless_symbol (x)) - { - if (! cris_pic_sympart_only) - fprintf (file, "$%s+", reg_names [PIC_OFFSET_TABLE_REGNUM]); - cris_output_addr_const (file, XEXP (x, 0)); + insn = emit_insn (gen_rtx_SET (VOIDmode, + stack_pointer_rtx, + plus_constant (stack_pointer_rtx, + -cfoa_size))); + RTX_FRAME_RELATED_P (insn) = 1; + framesize += cfoa_size; + } + } + else if ((size + cfoa_size) > 0) + { + insn = emit_insn (gen_rtx_SET (VOIDmode, + stack_pointer_rtx, + plus_constant (stack_pointer_rtx, + -(cfoa_size + size)))); + RTX_FRAME_RELATED_P (insn) = 1; + framesize += size + cfoa_size; + } - fprintf (file, ":GOTOFF"); - } - else - /* Labels are never marked as global symbols. */ - fatal_insn ("unexpected PIC symbol", x); + /* Set up the PIC register, if it is used. */ + if (got_really_used) + { + rtx got + = gen_rtx_UNSPEC (SImode, gen_rtvec (1, const0_rtx), CRIS_UNSPEC_GOT); + emit_move_insn (pic_offset_table_rtx, got); + + /* FIXME: This is a cover-up for flow2 messing up; it doesn't + follow exceptional paths and tries to delete the GOT load as + unused, if it isn't used on the non-exceptional paths. Other + ports have similar or other cover-ups, or plain bugs marking + the GOT register load as maybe-dead. To see this, remove the + line below and try libsupc++/vec.cc or a trivial + "static void y (); void x () {try {y ();} catch (...) {}}". */ + emit_use (pic_offset_table_rtx); + } + + if (cris_max_stackframe && framesize > cris_max_stackframe) + warning (0, "stackframe too big: %d bytes", framesize); +} - /* Sanity check. */ - if (! current_function_uses_pic_offset_table) - internal_error ("emitting PIC operand, but PIC register isn't set up"); +/* The expander for the epilogue pattern. */ + +void +cris_expand_epilogue (void) +{ + int regno; + int size = get_frame_size (); + int last_movem_reg = -1; + int argspace_offset = crtl->outgoing_args_size; + int pretend = crtl->args.pretend_args_size; + rtx mem; + bool return_address_on_stack = cris_return_address_on_stack (); + /* A reference may have been optimized out + (like the abort () in fde_split in unwind-dw2-fde.c, at least 3.2.1) + so check that it's still used. */ + int got_really_used = false; + int n_movem_regs = 0; + + if (!TARGET_PROLOGUE_EPILOGUE) + return; + + if (crtl->uses_pic_offset_table) + { + /* A reference may have been optimized out (like the abort () in + fde_split in unwind-dw2-fde.c, at least 3.2.1) so check that + it's still used. */ + push_topmost_sequence (); + got_really_used + = reg_used_between_p (pic_offset_table_rtx, get_insns (), NULL_RTX); + pop_topmost_sequence (); + } + + /* Align byte count of stack frame. */ + if (TARGET_STACK_ALIGN) + size = TARGET_ALIGN_BY_32 ? (size + 3) & ~3 : (size + 1) & ~1; + + /* Check how many saved regs we can movem. They start at r0 and must + be contiguous. */ + for (regno = 0; + regno < FIRST_PSEUDO_REGISTER; + regno++) + if (cris_reg_saved_in_regsave_area (regno, got_really_used)) + { + n_movem_regs++; + + if (regno == last_movem_reg + 1) + last_movem_reg = regno; + else break; + } + + /* If there was only one register that really needed to be saved + through movem, don't use movem. */ + if (n_movem_regs == 1) + last_movem_reg = -1; + + /* Now emit "normal" move insns for all regs higher than the movem + regs. */ + for (regno = FIRST_PSEUDO_REGISTER - 1; + regno > last_movem_reg; + regno--) + if (cris_reg_saved_in_regsave_area (regno, got_really_used)) + { + rtx insn; + + if (argspace_offset) + { + /* There is an area for outgoing parameters located before + the saved registers. We have to adjust for that. */ + emit_insn (gen_rtx_SET (VOIDmode, + stack_pointer_rtx, + plus_constant (stack_pointer_rtx, + argspace_offset))); + /* Make sure we only do this once. */ + argspace_offset = 0; + } + + mem = gen_rtx_MEM (SImode, gen_rtx_POST_INC (SImode, + stack_pointer_rtx)); + set_mem_alias_set (mem, get_frame_alias_set ()); + insn = emit_move_insn (gen_rtx_raw_REG (SImode, regno), mem); + + /* Whenever we emit insns with post-incremented addresses + ourselves, we must add a post-inc note manually. */ + REG_NOTES (insn) + = alloc_EXPR_LIST (REG_INC, stack_pointer_rtx, REG_NOTES (insn)); + } + + /* If we have any movem-restore, do it now. */ + if (last_movem_reg != -1) + { + rtx insn; + + if (argspace_offset) + { + emit_insn (gen_rtx_SET (VOIDmode, + stack_pointer_rtx, + plus_constant (stack_pointer_rtx, + argspace_offset))); + argspace_offset = 0; } - output_addr_const (file, x); - break; + mem = gen_rtx_MEM (SImode, + gen_rtx_POST_INC (SImode, stack_pointer_rtx)); + set_mem_alias_set (mem, get_frame_alias_set ()); + insn + = emit_insn (cris_gen_movem_load (mem, + GEN_INT (last_movem_reg + 1), 0)); + /* Whenever we emit insns with post-incremented addresses + ourselves, we must add a post-inc note manually. */ + if (side_effects_p (PATTERN (insn))) + REG_NOTES (insn) + = alloc_EXPR_LIST (REG_INC, stack_pointer_rtx, REG_NOTES (insn)); + } - case NOTE: - if (NOTE_LINE_NUMBER (x) != NOTE_INSN_DELETED_LABEL) - fatal_insn ("unexpected NOTE as addr_const:", x); - case CODE_LABEL: - case CONST_INT: - case CONST_DOUBLE: - case ZERO_EXTEND: - case SIGN_EXTEND: - output_addr_const (file, x); - break; + /* If we don't clobber all of the allocated stack area (we've already + deallocated saved registers), GCC might want to schedule loads from + the stack to *after* the stack-pointer restore, which introduces an + interrupt race condition. This happened for the initial-value + SRP-restore for g++.dg/eh/registers1.C (noticed by inspection of + other failure for that test). It also happened for the stack slot + for the return value in (one version of) + linux/fs/dcache.c:__d_lookup, at least with "-O2 + -fno-omit-frame-pointer". */ - case CONST: - /* This used to output parentheses around the expression, - but that does not work on the 386 (either ATT or BSD assembler). */ - cris_output_addr_const (file, XEXP (x, 0)); - break; + /* Restore frame pointer if necessary. */ + if (frame_pointer_needed) + { + rtx insn; - case PLUS: - /* Some assemblers need integer constants to appear last (eg masm). */ - if (GET_CODE (XEXP (x, 0)) == CONST_INT) + emit_insn (gen_cris_frame_deallocated_barrier ()); + + emit_move_insn (stack_pointer_rtx, frame_pointer_rtx); + mem = gen_rtx_MEM (SImode, gen_rtx_POST_INC (SImode, + stack_pointer_rtx)); + set_mem_alias_set (mem, get_frame_alias_set ()); + insn = emit_move_insn (frame_pointer_rtx, mem); + + /* Whenever we emit insns with post-incremented addresses + ourselves, we must add a post-inc note manually. */ + REG_NOTES (insn) + = alloc_EXPR_LIST (REG_INC, stack_pointer_rtx, REG_NOTES (insn)); + } + else if ((size + argspace_offset) != 0) + { + emit_insn (gen_cris_frame_deallocated_barrier ()); + + /* If there was no frame-pointer to restore sp from, we must + explicitly deallocate local variables. */ + + /* Handle space for outgoing parameters that hasn't been handled + yet. */ + size += argspace_offset; + + emit_insn (gen_rtx_SET (VOIDmode, + stack_pointer_rtx, + plus_constant (stack_pointer_rtx, size))); + } + + /* If this function has no pushed register parameters + (stdargs/varargs), and if it is not a leaf function, then we have + the return address on the stack. */ + if (return_address_on_stack && pretend == 0) + { + if (TARGET_V32 || crtl->calls_eh_return) { - cris_output_addr_const (file, XEXP (x, 1)); - if (INTVAL (XEXP (x, 0)) >= 0) - fprintf (file, "+"); - output_addr_const (file, XEXP (x, 0)); + rtx mem; + rtx insn; + rtx srpreg = gen_rtx_raw_REG (SImode, CRIS_SRP_REGNUM); + mem = gen_rtx_MEM (SImode, + gen_rtx_POST_INC (SImode, + stack_pointer_rtx)); + set_mem_alias_set (mem, get_frame_alias_set ()); + insn = emit_move_insn (srpreg, mem); + + /* Whenever we emit insns with post-incremented addresses + ourselves, we must add a post-inc note manually. */ + REG_NOTES (insn) + = alloc_EXPR_LIST (REG_INC, stack_pointer_rtx, REG_NOTES (insn)); + + if (crtl->calls_eh_return) + emit_insn (gen_addsi3 (stack_pointer_rtx, + stack_pointer_rtx, + gen_rtx_raw_REG (SImode, + CRIS_STACKADJ_REG))); + cris_expand_return (false); } else + cris_expand_return (true); + + return; + } + + /* If we pushed some register parameters, then adjust the stack for + them. */ + if (pretend != 0) + { + /* If SRP is stored on the way, we need to restore it first. */ + if (return_address_on_stack) { - cris_output_addr_const (file, XEXP (x, 0)); - if (GET_CODE (XEXP (x, 1)) != CONST_INT - || INTVAL (XEXP (x, 1)) >= 0) - fprintf (file, "+"); - cris_output_addr_const (file, XEXP (x, 1)); + rtx mem; + rtx srpreg = gen_rtx_raw_REG (SImode, CRIS_SRP_REGNUM); + rtx insn; + + mem = gen_rtx_MEM (SImode, + gen_rtx_POST_INC (SImode, + stack_pointer_rtx)); + set_mem_alias_set (mem, get_frame_alias_set ()); + insn = emit_move_insn (srpreg, mem); + + /* Whenever we emit insns with post-incremented addresses + ourselves, we must add a post-inc note manually. */ + REG_NOTES (insn) + = alloc_EXPR_LIST (REG_INC, stack_pointer_rtx, REG_NOTES (insn)); } - break; - case MINUS: - /* Avoid outputting things like x-x or x+5-x, - since some assemblers can't handle that. */ - x = simplify_subtraction (x); - if (GET_CODE (x) != MINUS) - goto restart; - - cris_output_addr_const (file, XEXP (x, 0)); - fprintf (file, "-"); - if ((GET_CODE (XEXP (x, 1)) == CONST_INT - && INTVAL (XEXP (x, 1)) < 0) - || GET_CODE (XEXP (x, 1)) != CONST_INT) + emit_insn (gen_rtx_SET (VOIDmode, + stack_pointer_rtx, + plus_constant (stack_pointer_rtx, pretend))); + } + + /* Perform the "physical" unwinding that the EH machinery calculated. */ + if (crtl->calls_eh_return) + emit_insn (gen_addsi3 (stack_pointer_rtx, + stack_pointer_rtx, + gen_rtx_raw_REG (SImode, + CRIS_STACKADJ_REG))); + cris_expand_return (false); +} + +/* Worker function for generating movem from mem for load_multiple. */ + +rtx +cris_gen_movem_load (rtx src, rtx nregs_rtx, int nprefix) +{ + int nregs = INTVAL (nregs_rtx); + rtvec vec; + int eltno = 1; + int i; + rtx srcreg = XEXP (src, 0); + unsigned int regno = nregs - 1; + int regno_inc = -1; + + if (TARGET_V32) + { + regno = 0; + regno_inc = 1; + } + + if (GET_CODE (srcreg) == POST_INC) + srcreg = XEXP (srcreg, 0); + + CRIS_ASSERT (REG_P (srcreg)); + + /* Don't use movem for just one insn. The insns are equivalent except + for the pipeline hazard (on v32); movem does not forward the loaded + registers so there's a three cycles penalty for their use. */ + if (nregs == 1) + return gen_movsi (gen_rtx_REG (SImode, 0), src); + + vec = rtvec_alloc (nprefix + nregs + + (GET_CODE (XEXP (src, 0)) == POST_INC)); + + if (GET_CODE (XEXP (src, 0)) == POST_INC) + { + RTVEC_ELT (vec, nprefix + 1) + = gen_rtx_SET (VOIDmode, srcreg, plus_constant (srcreg, nregs * 4)); + eltno++; + } + + src = replace_equiv_address (src, srcreg); + RTVEC_ELT (vec, nprefix) + = gen_rtx_SET (VOIDmode, gen_rtx_REG (SImode, regno), src); + regno += regno_inc; + + for (i = 1; i < nregs; i++, eltno++) + { + RTVEC_ELT (vec, nprefix + eltno) + = gen_rtx_SET (VOIDmode, gen_rtx_REG (SImode, regno), + adjust_address_nv (src, SImode, i * 4)); + regno += regno_inc; + } + + return gen_rtx_PARALLEL (VOIDmode, vec); +} + +/* Worker function for generating movem to mem. If FRAME_RELATED, notes + are added that the dwarf2 machinery understands. */ + +rtx +cris_emit_movem_store (rtx dest, rtx nregs_rtx, int increment, + bool frame_related) +{ + int nregs = INTVAL (nregs_rtx); + rtvec vec; + int eltno = 1; + int i; + rtx insn; + rtx destreg = XEXP (dest, 0); + unsigned int regno = nregs - 1; + int regno_inc = -1; + + if (TARGET_V32) + { + regno = 0; + regno_inc = 1; + } + + if (GET_CODE (destreg) == POST_INC) + increment += nregs * 4; + + if (GET_CODE (destreg) == POST_INC || GET_CODE (destreg) == PLUS) + destreg = XEXP (destreg, 0); + + CRIS_ASSERT (REG_P (destreg)); + + /* Don't use movem for just one insn. The insns are equivalent except + for the pipeline hazard (on v32); movem does not forward the loaded + registers so there's a three cycles penalty for use. */ + if (nregs == 1) + { + rtx mov = gen_rtx_SET (VOIDmode, dest, gen_rtx_REG (SImode, 0)); + + if (increment == 0) { - fprintf (file, "%s", targetm.asm_out.open_paren); - cris_output_addr_const (file, XEXP (x, 1)); - fprintf (file, "%s", targetm.asm_out.close_paren); + insn = emit_insn (mov); + if (frame_related) + RTX_FRAME_RELATED_P (insn) = 1; + return insn; } - else - output_addr_const (file, XEXP (x, 1)); - break; - default: - LOSE_AND_RETURN ("unexpected address expression", x); + /* If there was a request for a side-effect, create the ordinary + parallel. */ + vec = rtvec_alloc (2); + + RTVEC_ELT (vec, 0) = mov; + RTVEC_ELT (vec, 1) = gen_rtx_SET (VOIDmode, destreg, + plus_constant (destreg, increment)); + if (frame_related) + { + RTX_FRAME_RELATED_P (mov) = 1; + RTX_FRAME_RELATED_P (RTVEC_ELT (vec, 1)) = 1; + } + } + else + { + vec = rtvec_alloc (nregs + (increment != 0 ? 1 : 0)); + RTVEC_ELT (vec, 0) + = gen_rtx_SET (VOIDmode, + replace_equiv_address (dest, + plus_constant (destreg, + increment)), + gen_rtx_REG (SImode, regno)); + regno += regno_inc; + + /* The dwarf2 info wants this mark on each component in a parallel + that's part of the prologue (though it's optional on the first + component). */ + if (frame_related) + RTX_FRAME_RELATED_P (RTVEC_ELT (vec, 0)) = 1; + + if (increment != 0) + { + RTVEC_ELT (vec, 1) + = gen_rtx_SET (VOIDmode, destreg, + plus_constant (destreg, + increment != 0 + ? increment : nregs * 4)); + eltno++; + + if (frame_related) + RTX_FRAME_RELATED_P (RTVEC_ELT (vec, 1)) = 1; + + /* Don't call adjust_address_nv on a post-incremented address if + we can help it. */ + if (GET_CODE (XEXP (dest, 0)) == POST_INC) + dest = replace_equiv_address (dest, destreg); + } + + for (i = 1; i < nregs; i++, eltno++) + { + RTVEC_ELT (vec, eltno) + = gen_rtx_SET (VOIDmode, adjust_address_nv (dest, SImode, i * 4), + gen_rtx_REG (SImode, regno)); + if (frame_related) + RTX_FRAME_RELATED_P (RTVEC_ELT (vec, eltno)) = 1; + regno += regno_inc; + } + } + + insn = emit_insn (gen_rtx_PARALLEL (VOIDmode, vec)); + + /* Because dwarf2out.c handles the insns in a parallel as a sequence, + we need to keep the stack adjustment separate, after the + MEM-setters. Else the stack-adjustment in the second component of + the parallel would be mishandled; the offsets for the SETs that + follow it would be wrong. We prepare for this by adding a + REG_FRAME_RELATED_EXPR with the MEM-setting parts in a SEQUENCE + followed by the increment. Note that we have FRAME_RELATED_P on + all the SETs, including the original stack adjustment SET in the + parallel. */ + if (frame_related) + { + if (increment != 0) + { + rtx seq = gen_rtx_SEQUENCE (VOIDmode, rtvec_alloc (nregs + 1)); + XVECEXP (seq, 0, 0) = copy_rtx (XVECEXP (PATTERN (insn), 0, 0)); + for (i = 1; i < nregs; i++) + XVECEXP (seq, 0, i) + = copy_rtx (XVECEXP (PATTERN (insn), 0, i + 1)); + XVECEXP (seq, 0, nregs) = copy_rtx (XVECEXP (PATTERN (insn), 0, 1)); + REG_NOTES (insn) + = gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR, seq, + REG_NOTES (insn)); + } + + RTX_FRAME_RELATED_P (insn) = 1; } + + return insn; } -/* The ENCODE_SECTION_INFO worker. Code-in whether we can get away - without a GOT entry (needed for externally visible objects but not for - functions) into SYMBOL_REF_FLAG and add the PLT suffix for global - functions. */ +/* Worker function for expanding the address for PIC function calls. */ void -cris_encode_section_info (exp) - tree exp; +cris_expand_pic_call_address (rtx *opp) { - if (flag_pic) + rtx op = *opp; + + gcc_assert (MEM_P (op)); + op = XEXP (op, 0); + + /* It might be that code can be generated that jumps to 0 (or to a + specific address). Don't die on that. (There is a + testcase.) */ + if (CONSTANT_ADDRESS_P (op) && !CONST_INT_P (op)) { - if (DECL_P (exp)) + enum cris_pic_symbol_type t = cris_pic_symbol_type_of (op); + + CRIS_ASSERT (can_create_pseudo_p ()); + + /* For local symbols (non-PLT), just get the plain symbol + reference into a register. For symbols that can be PLT, make + them PLT. */ + if (t == cris_rel_symbol) + { + /* For v32, we're fine as-is; just PICify the symbol. Forcing + into a register caused performance regression for 3.2.1, + observable in __floatdidf and elsewhere in libgcc. */ + if (TARGET_V32) + { + rtx sym = GET_CODE (op) != CONST ? op : get_related_value (op); + HOST_WIDE_INT offs = get_integer_term (op); + + /* We can't get calls to sym+N, N integer, can we? */ + gcc_assert (offs == 0); + + op = gen_rtx_CONST (Pmode, + gen_rtx_UNSPEC (Pmode, gen_rtvec (1, sym), + CRIS_UNSPEC_PCREL)); + } + else + op = force_reg (Pmode, op); + } + else if (t == cris_got_symbol) { - if (TREE_CODE (exp) == FUNCTION_DECL - && (TREE_PUBLIC (exp) || DECL_WEAK (exp))) - SYMBOL_REF_FLAG (XEXP (DECL_RTL (exp), 0)) = 0; + if (TARGET_AVOID_GOTPLT) + { + /* Change a "jsr sym" into (allocate register rM, rO) + "move.d (const (unspec [sym rPIC] CRIS_UNSPEC_PLT_GOTREL)),rM" + "add.d rPIC,rM,rO", "jsr rO" for pre-v32 and + "jsr (const (unspec [sym rPIC] CRIS_UNSPEC_PLT_PCREL))" + for v32. */ + rtx tem, rm, ro; + gcc_assert (can_create_pseudo_p ()); + crtl->uses_pic_offset_table = 1; + tem = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, op), + TARGET_V32 + ? CRIS_UNSPEC_PLT_PCREL + : CRIS_UNSPEC_PLT_GOTREL); + tem = gen_rtx_CONST (Pmode, tem); + if (TARGET_V32) + op = tem; + else + { + rm = gen_reg_rtx (Pmode); + emit_move_insn (rm, tem); + ro = gen_reg_rtx (Pmode); + if (expand_binop (Pmode, add_optab, rm, + pic_offset_table_rtx, + ro, 0, OPTAB_LIB_WIDEN) != ro) + internal_error ("expand_binop failed in movsi got"); + op = ro; + } + } else - SYMBOL_REF_FLAG (XEXP (DECL_RTL (exp), 0)) - = ! TREE_PUBLIC (exp) && ! DECL_WEAK (exp); + { + /* Change a "jsr sym" into (allocate register rM, rO) + "move.d (const (unspec [sym] CRIS_UNSPEC_PLTGOTREAD)),rM" + "add.d rPIC,rM,rO" "jsr [rO]" with the memory access + marked as not trapping and not aliasing. No "move.d + [rO],rP" as that would invite to re-use of a value + that should not be reused. FIXME: Need a peephole2 + for cases when this is cse:d from the call, to change + back to just get the PLT entry address, so we don't + resolve the same symbol over and over (the memory + access of the PLTGOT isn't constant). */ + rtx tem, mem, rm, ro; + + gcc_assert (can_create_pseudo_p ()); + crtl->uses_pic_offset_table = 1; + tem = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, op), + CRIS_UNSPEC_PLTGOTREAD); + rm = gen_reg_rtx (Pmode); + emit_move_insn (rm, gen_rtx_CONST (Pmode, tem)); + ro = gen_reg_rtx (Pmode); + if (expand_binop (Pmode, add_optab, rm, + pic_offset_table_rtx, + ro, 0, OPTAB_LIB_WIDEN) != ro) + internal_error ("expand_binop failed in movsi got"); + mem = gen_rtx_MEM (Pmode, ro); + + /* This MEM doesn't alias anything. Whether it aliases + other same symbols is unimportant. */ + set_mem_alias_set (mem, new_alias_set ()); + MEM_NOTRAP_P (mem) = 1; + op = mem; + } } else - /* Others are local entities. */ - SYMBOL_REF_FLAG (XEXP (TREE_CST_RTL (exp), 0)) = 1; + /* Can't possibly get a GOT-needing-fixup for a function-call, + right? */ + fatal_insn ("Unidentifiable call op", op); + + *opp = replace_equiv_address (*opp, op); + } +} + +/* Make sure operands are in the right order for an addsi3 insn as + generated by a define_split. Nothing but REG_P as the first + operand is recognized by addsi3 after reload. OPERANDS contains + the operands, with the first at OPERANDS[N] and the second at + OPERANDS[N+1]. */ + +void +cris_order_for_addsi3 (rtx *operands, int n) +{ + if (!REG_P (operands[n])) + { + rtx tem = operands[n]; + operands[n] = operands[n + 1]; + operands[n + 1] = tem; + } +} + +/* Use from within code, from e.g. PRINT_OPERAND and + PRINT_OPERAND_ADDRESS. Macros used in output_addr_const need to emit + different things depending on whether code operand or constant is + emitted. */ + +static void +cris_output_addr_const (FILE *file, rtx x) +{ + in_code++; + output_addr_const (file, x); + in_code--; +} + +/* Worker function for ASM_OUTPUT_SYMBOL_REF. */ + +void +cris_asm_output_symbol_ref (FILE *file, rtx x) +{ + gcc_assert (GET_CODE (x) == SYMBOL_REF); + + if (flag_pic && in_code > 0) + { + const char *origstr = XSTR (x, 0); + const char *str; + str = (* targetm.strip_name_encoding) (origstr); + assemble_name (file, str); + + /* Sanity check. */ + if (!TARGET_V32 && !crtl->uses_pic_offset_table) + output_operand_lossage ("PIC register isn't set up"); + } + else + assemble_name (file, XSTR (x, 0)); +} + +/* Worker function for ASM_OUTPUT_LABEL_REF. */ + +void +cris_asm_output_label_ref (FILE *file, char *buf) +{ + if (flag_pic && in_code > 0) + { + assemble_name (file, buf); + + /* Sanity check. */ + if (!TARGET_V32 && !crtl->uses_pic_offset_table) + internal_error ("emitting PIC operand, but PIC register isn't set up"); + } + else + assemble_name (file, buf); +} + +/* Worker function for OUTPUT_ADDR_CONST_EXTRA. */ + +bool +cris_output_addr_const_extra (FILE *file, rtx xconst) +{ + switch (GET_CODE (xconst)) + { + rtx x; + + case UNSPEC: + x = XVECEXP (xconst, 0, 0); + CRIS_ASSERT (GET_CODE (x) == SYMBOL_REF + || GET_CODE (x) == LABEL_REF + || GET_CODE (x) == CONST); + output_addr_const (file, x); + switch (XINT (xconst, 1)) + { + case CRIS_UNSPEC_PCREL: + /* We only get this with -fpic/PIC to tell it apart from an + invalid symbol. We can't tell here, but it should only + be the operand of a call or movsi. */ + gcc_assert (TARGET_V32 && flag_pic); + break; + + case CRIS_UNSPEC_PLT_PCREL: + gcc_assert (TARGET_V32); + fprintf (file, ":PLT"); + break; + + case CRIS_UNSPEC_PLT_GOTREL: + gcc_assert (!TARGET_V32); + fprintf (file, ":PLTG"); + break; + + case CRIS_UNSPEC_GOTREL: + gcc_assert (!TARGET_V32); + fprintf (file, ":GOTOFF"); + break; + + case CRIS_UNSPEC_GOTREAD: + if (flag_pic == 1) + fprintf (file, ":GOT16"); + else + fprintf (file, ":GOT"); + break; + + case CRIS_UNSPEC_PLTGOTREAD: + if (flag_pic == 1) + fprintf (file, CRIS_GOTPLT_SUFFIX "16"); + else + fprintf (file, CRIS_GOTPLT_SUFFIX); + break; + + default: + gcc_unreachable (); + } + return true; + + default: + return false; + } +} + +/* Worker function for TARGET_STRUCT_VALUE_RTX. */ + +static rtx +cris_struct_value_rtx (tree fntype ATTRIBUTE_UNUSED, + int incoming ATTRIBUTE_UNUSED) +{ + return gen_rtx_REG (Pmode, CRIS_STRUCT_VALUE_REGNUM); +} + +/* Worker function for TARGET_SETUP_INCOMING_VARARGS. */ + +static void +cris_setup_incoming_varargs (CUMULATIVE_ARGS *ca, + enum machine_mode mode ATTRIBUTE_UNUSED, + tree type ATTRIBUTE_UNUSED, + int *pretend_arg_size, + int second_time) +{ + if (ca->regs < CRIS_MAX_ARGS_IN_REGS) + { + int stdarg_regs = CRIS_MAX_ARGS_IN_REGS - ca->regs; + cfun->machine->stdarg_regs = stdarg_regs; + *pretend_arg_size = stdarg_regs * 4; + } + + if (TARGET_PDEBUG) + fprintf (asm_out_file, + "\n; VA:: ANSI: %d args before, anon @ #%d, %dtime\n", + ca->regs, *pretend_arg_size, second_time); +} + +/* Return true if TYPE must be passed by invisible reference. + For cris, we pass <= 8 bytes by value, others by reference. */ + +static bool +cris_pass_by_reference (CUMULATIVE_ARGS *ca ATTRIBUTE_UNUSED, + enum machine_mode mode, const_tree type, + bool named ATTRIBUTE_UNUSED) +{ + return (targetm.calls.must_pass_in_stack (mode, type) + || CRIS_FUNCTION_ARG_SIZE (mode, type) > 8); +} + + +static int +cris_arg_partial_bytes (CUMULATIVE_ARGS *ca, enum machine_mode mode, + tree type, bool named ATTRIBUTE_UNUSED) +{ + if (ca->regs == CRIS_MAX_ARGS_IN_REGS - 1 + && !targetm.calls.must_pass_in_stack (mode, type) + && CRIS_FUNCTION_ARG_SIZE (mode, type) > 4 + && CRIS_FUNCTION_ARG_SIZE (mode, type) <= 8) + return UNITS_PER_WORD; + else + return 0; +} + +/* Worker function for TARGET_MD_ASM_CLOBBERS. */ + +static tree +cris_md_asm_clobbers (tree outputs, tree inputs, tree in_clobbers) +{ + HARD_REG_SET mof_set; + tree clobbers; + tree t; + + CLEAR_HARD_REG_SET (mof_set); + SET_HARD_REG_BIT (mof_set, CRIS_MOF_REGNUM); + + /* For the time being, all asms clobber condition codes. Revisit when + there's a reasonable use for inputs/outputs that mention condition + codes. */ + clobbers + = tree_cons (NULL_TREE, + build_string (strlen (reg_names[CRIS_CC0_REGNUM]), + reg_names[CRIS_CC0_REGNUM]), + in_clobbers); + + for (t = outputs; t != NULL; t = TREE_CHAIN (t)) + { + tree val = TREE_VALUE (t); + + /* The constraint letter for the singleton register class of MOF + is 'h'. If it's mentioned in the constraints, the asm is + MOF-aware and adding it to the clobbers would cause it to have + impossible constraints. */ + if (strchr (TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (t))), + 'h') != NULL + || tree_overlaps_hard_reg_set (val, &mof_set) != NULL_TREE) + return clobbers; } + + for (t = inputs; t != NULL; t = TREE_CHAIN (t)) + { + tree val = TREE_VALUE (t); + + if (strchr (TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (t))), + 'h') != NULL + || tree_overlaps_hard_reg_set (val, &mof_set) != NULL_TREE) + return clobbers; + } + + return tree_cons (NULL_TREE, + build_string (strlen (reg_names[CRIS_MOF_REGNUM]), + reg_names[CRIS_MOF_REGNUM]), + clobbers); } #if 0 @@ -3072,49 +3809,42 @@ cris_encode_section_info (exp) debugger. They might collide with gcc functions or system functions, so only emit them when '#if 1' above. */ -enum rtx_code Get_code PARAMS ((rtx)); +enum rtx_code Get_code (rtx); enum rtx_code -Get_code (x) - rtx x; +Get_code (rtx x) { return GET_CODE (x); } -const char *Get_mode PARAMS ((rtx)); +const char *Get_mode (rtx); const char * -Get_mode (x) - rtx x; +Get_mode (rtx x) { return GET_MODE_NAME (GET_MODE (x)); } -rtx Xexp PARAMS ((rtx, int)); +rtx Xexp (rtx, int); rtx -Xexp (x, n) - rtx x; - int n; +Xexp (rtx x, int n) { return XEXP (x, n); } -rtx Xvecexp PARAMS ((rtx, int, int)); +rtx Xvecexp (rtx, int, int); rtx -Xvecexp (x, n, m) - rtx x; - int n; +Xvecexp (rtx x, int n, int m) { return XVECEXP (x, n, m); } -int Get_rtx_len PARAMS ((rtx)); +int Get_rtx_len (rtx); int -Get_rtx_len (x) - rtx x; +Get_rtx_len (rtx x) { return GET_RTX_LENGTH (GET_CODE (x)); } @@ -3122,25 +3852,25 @@ Get_rtx_len (x) /* Use upper-case to distinguish from local variables that are sometimes called next_insn and prev_insn. */ -rtx Next_insn PARAMS ((rtx)); +rtx Next_insn (rtx); rtx -Next_insn (insn) - rtx insn; +Next_insn (rtx insn) { return NEXT_INSN (insn); } -rtx Prev_insn PARAMS ((rtx)); +rtx Prev_insn (rtx); rtx -Prev_insn (insn) - rtx insn; +Prev_insn (rtx insn) { return PREV_INSN (insn); } #endif +#include "gt-cris.h" + /* * Local variables: * eval: (c-set-style "gnu")