X-Git-Url: http://git.sourceforge.jp/view?p=pf3gnuchains%2Fgcc-fork.git;a=blobdiff_plain;f=gcc%2Fconfig%2Fm32r%2Fm32r.c;h=dd86186d56d3c5a34134ba15e1a70adf16302913;hp=ddc052b365bb3ecb709e0cc3916747c0d89b69e1;hb=deceffc1405fb824feffc766c8d5002c3ce41c82;hpb=d7f038f87955f9782e74b6793c0abffc7475d984 diff --git a/gcc/config/m32r/m32r.c b/gcc/config/m32r/m32r.c index ddc052b365b..dd86186d56d 100644 --- a/gcc/config/m32r/m32r.c +++ b/gcc/config/m32r/m32r.c @@ -1,25 +1,28 @@ -/* Subroutines used for code generation on the Mitsubishi M32R cpu. - Copyright (C) 1996, 1997, 1998 Free Software Foundation, Inc. +/* Subroutines used for code generation on the Renesas M32R cpu. + Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, + 2005 Free Software Foundation, Inc. -This file is part of GNU CC. + This file is part of GCC. -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2, or (at your option) -any later version. + 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) any later version. -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. + GCC is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + License for more details. -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 59 Temple Place - Suite 330, -Boston, MA 02111-1307, USA. */ + 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. */ #include "config.h" #include "system.h" +#include "coretypes.h" +#include "tm.h" #include "tree.h" #include "rtl.h" #include "regs.h" @@ -27,12 +30,18 @@ Boston, MA 02111-1307, USA. */ #include "real.h" #include "insn-config.h" #include "conditions.h" -#include "insn-flags.h" #include "output.h" #include "insn-attr.h" #include "flags.h" #include "expr.h" +#include "function.h" #include "recog.h" +#include "toplev.h" +#include "ggc.h" +#include "integrate.h" +#include "tm_p.h" +#include "target.h" +#include "target-def.h" /* Save the operands last given to a compare for use when we generate a scc or bcc insn. */ @@ -42,21 +51,111 @@ rtx m32r_compare_op0, m32r_compare_op1; char m32r_punct_chars[256]; /* Selected code model. */ -char *m32r_model_string = M32R_MODEL_DEFAULT; +const char * m32r_model_string = M32R_MODEL_DEFAULT; enum m32r_model m32r_model; /* Selected SDA support. */ -char *m32r_sdata_string = M32R_SDATA_DEFAULT; +const char * m32r_sdata_string = M32R_SDATA_DEFAULT; enum m32r_sdata m32r_sdata; +/* Machine-specific symbol_ref flags. */ +#define SYMBOL_FLAG_MODEL_SHIFT SYMBOL_FLAG_MACH_DEP_SHIFT +#define SYMBOL_REF_MODEL(X) \ + ((enum m32r_model) ((SYMBOL_REF_FLAGS (X) >> SYMBOL_FLAG_MODEL_SHIFT) & 3)) + +/* For string literals, etc. */ +#define LIT_NAME_P(NAME) ((NAME)[0] == '*' && (NAME)[1] == '.') + +/* Cache-flush support. Cache-flush is used at trampoline. + Default cache-flush is "trap 12". + default cache-flush function is "_flush_cache" (CACHE_FLUSH_FUNC) + default cache-flush trap-interrupt number is "12". (CACHE_FLUSH_TRAP) + You can change how to generate code of cache-flush with following options. + -flush-func=FLUSH-FUNC-NAME + -no-flush-func + -fluch-trap=TRAP-NUMBER + -no-flush-trap. */ +const char *m32r_cache_flush_func = CACHE_FLUSH_FUNC; +const char *m32r_cache_flush_trap_string = CACHE_FLUSH_TRAP; +int m32r_cache_flush_trap = 12; /* Forward declaration. */ -static void init_reg_tables PROTO((void)); - +static void init_reg_tables (void); +static void block_move_call (rtx, rtx, rtx); +static int m32r_is_insn (rtx); +const struct attribute_spec m32r_attribute_table[]; +static tree m32r_handle_model_attribute (tree *, tree, tree, int, bool *); +static void m32r_output_function_prologue (FILE *, HOST_WIDE_INT); +static void m32r_output_function_epilogue (FILE *, HOST_WIDE_INT); + +static void m32r_file_start (void); + +static int m32r_adjust_priority (rtx, int); +static int m32r_issue_rate (void); + +static void m32r_encode_section_info (tree, rtx, int); +static bool m32r_in_small_data_p (tree); +static bool m32r_return_in_memory (tree, tree); +static void m32r_setup_incoming_varargs (CUMULATIVE_ARGS *, enum machine_mode, + tree, int *, int); +static void init_idents (void); +static bool m32r_rtx_costs (rtx, int, int, int *); +static bool m32r_pass_by_reference (CUMULATIVE_ARGS *, enum machine_mode, + tree, bool); +static int m32r_arg_partial_bytes (CUMULATIVE_ARGS *, enum machine_mode, + tree, bool); + +/* Initialize the GCC target structure. */ +#undef TARGET_ATTRIBUTE_TABLE +#define TARGET_ATTRIBUTE_TABLE m32r_attribute_table + +#undef TARGET_ASM_ALIGNED_HI_OP +#define TARGET_ASM_ALIGNED_HI_OP "\t.hword\t" +#undef TARGET_ASM_ALIGNED_SI_OP +#define TARGET_ASM_ALIGNED_SI_OP "\t.word\t" + +#undef TARGET_ASM_FUNCTION_PROLOGUE +#define TARGET_ASM_FUNCTION_PROLOGUE m32r_output_function_prologue +#undef TARGET_ASM_FUNCTION_EPILOGUE +#define TARGET_ASM_FUNCTION_EPILOGUE m32r_output_function_epilogue + +#undef TARGET_ASM_FILE_START +#define TARGET_ASM_FILE_START m32r_file_start + +#undef TARGET_SCHED_ADJUST_PRIORITY +#define TARGET_SCHED_ADJUST_PRIORITY m32r_adjust_priority +#undef TARGET_SCHED_ISSUE_RATE +#define TARGET_SCHED_ISSUE_RATE m32r_issue_rate + +#undef TARGET_ENCODE_SECTION_INFO +#define TARGET_ENCODE_SECTION_INFO m32r_encode_section_info +#undef TARGET_IN_SMALL_DATA_P +#define TARGET_IN_SMALL_DATA_P m32r_in_small_data_p + +#undef TARGET_RTX_COSTS +#define TARGET_RTX_COSTS m32r_rtx_costs +#undef TARGET_ADDRESS_COST +#define TARGET_ADDRESS_COST hook_int_rtx_0 + +#undef TARGET_PROMOTE_PROTOTYPES +#define TARGET_PROMOTE_PROTOTYPES hook_bool_tree_true +#undef TARGET_RETURN_IN_MEMORY +#define TARGET_RETURN_IN_MEMORY m32r_return_in_memory +#undef TARGET_SETUP_INCOMING_VARARGS +#define TARGET_SETUP_INCOMING_VARARGS m32r_setup_incoming_varargs +#undef TARGET_MUST_PASS_IN_STACK +#define TARGET_MUST_PASS_IN_STACK must_pass_in_stack_var_size +#undef TARGET_PASS_BY_REFERENCE +#define TARGET_PASS_BY_REFERENCE m32r_pass_by_reference +#undef TARGET_ARG_PARTIAL_BYTES +#define TARGET_ARG_PARTIAL_BYTES m32r_arg_partial_bytes + +struct gcc_target targetm = TARGET_INITIALIZER; + /* Called by OVERRIDE_OPTIONS to initialize various things. */ void -m32r_init () +m32r_init (void) { init_reg_tables (); @@ -87,6 +186,14 @@ m32r_init () else error ("bad value (%s) for -msdata switch", m32r_sdata_string); + if (m32r_cache_flush_trap_string) + { + /* Change trap-number (12) for cache-flush to the others (0 - 15). */ + m32r_cache_flush_trap = atoi (m32r_cache_flush_trap_string); + if (m32r_cache_flush_trap < 0 || m32r_cache_flush_trap > 15) + error ("bad value (%s) for -flush-trap=n (0= 0 && size <= g_switch_value) - prefix = SDATA_FLAG_CHAR; - } - } + abort (); /* shouldn't happen */ } + extra_flags |= model << SYMBOL_FLAG_MODEL_SHIFT; - /* If data area not decided yet, check for a code model. */ - if (prefix == 0) - { - if (model) - { - tree id; - - init_idents (); + if (extra_flags) + SYMBOL_REF_FLAGS (XEXP (rtl, 0)) |= extra_flags; +} - id = TREE_VALUE (TREE_VALUE (model)); +/* Only mark the object as being small data area addressable if + it hasn't been explicitly marked with a code model. - if (id == small_ident1 || id == small_ident2) - ; /* don't mark the symbol specially */ - else if (id == medium_ident1 || id == medium_ident2) - prefix = MEDIUM_FLAG_CHAR; - else if (id == large_ident1 || id == large_ident2) - prefix = LARGE_FLAG_CHAR; - else - abort (); /* shouldn't happen */ - } - else + The user can explicitly put an object in the small data area with the + section attribute. If the object is in sdata/sbss and marked with a + code model do both [put the object in .sdata and mark it as being + addressed with a specific code model - don't mark it as being addressed + with an SDA reloc though]. This is ok and might be useful at times. If + the object doesn't fit the linker will give an error. */ + +static bool +m32r_in_small_data_p (tree decl) +{ + tree section; + + if (TREE_CODE (decl) != VAR_DECL) + return false; + + if (lookup_attribute ("model", DECL_ATTRIBUTES (decl))) + return false; + + section = DECL_SECTION_NAME (decl); + if (section) + { + char *name = (char *) TREE_STRING_POINTER (section); + if (strcmp (name, ".sdata") == 0 || strcmp (name, ".sbss") == 0) + return true; + } + else + { + if (! TREE_READONLY (decl) && ! TARGET_SDATA_NONE) { - if (TARGET_MODEL_SMALL) - ; /* don't mark the symbol specially */ - else if (TARGET_MODEL_MEDIUM) - prefix = MEDIUM_FLAG_CHAR; - else if (TARGET_MODEL_LARGE) - prefix = LARGE_FLAG_CHAR; - else - abort (); /* shouldn't happen */ + int size = int_size_in_bytes (TREE_TYPE (decl)); + + if (size > 0 && (unsigned HOST_WIDE_INT) size <= g_switch_value) + return true; } } - if (prefix != 0) - { - rtx rtl = (TREE_CODE_CLASS (TREE_CODE (decl)) != 'd' - ? TREE_CST_RTL (decl) : DECL_RTL (decl)); - char *str = XSTR (XEXP (rtl, 0), 0); - int len = strlen (str); - char *newstr = savealloc (len + 2); - strcpy (newstr + 1, str); - *newstr = prefix; - XSTR (XEXP (rtl, 0), 0) = newstr; - } + return false; } /* Do anything needed before RTL is emitted for each function. */ void -m32r_init_expanders () +m32r_init_expanders (void) { /* ??? At one point there was code here. The function is left in to make it easy to experiment. */ } -/* Acceptable arguments to the call insn. */ - -int -call_address_operand (op, int_mode) - rtx op; - int int_mode; -{ - return symbolic_operand (op, int_mode); - -/* Constants and values in registers are not OK, because - the m32r BL instruction can only support PC relative branching. */ -} - int -call_operand (op, int_mode) - rtx op; - int int_mode; +call_operand (rtx op, enum machine_mode mode) { - enum machine_mode mode = (enum machine_mode)int_mode; - if (GET_CODE (op) != MEM) return 0; op = XEXP (op, 0); return call_address_operand (op, mode); } -/* Returns 1 if OP is a symbol reference. */ - -int -symbolic_operand (op, int_mode) - rtx op; - int int_mode; -{ - switch (GET_CODE (op)) - { - case SYMBOL_REF: - case LABEL_REF: - case CONST : - return 1; - - default: - return 0; - } -} - /* Return 1 if OP is a reference to an object in .sdata/.sbss. */ int -small_data_operand (op, int_mode) - rtx op; - int int_mode; +small_data_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) { if (! TARGET_SDATA_USE) return 0; if (GET_CODE (op) == SYMBOL_REF) - return SDATA_NAME_P (XSTR (op, 0)); + return SYMBOL_REF_SMALL_P (op); if (GET_CODE (op) == CONST && GET_CODE (XEXP (op, 0)) == PLUS && GET_CODE (XEXP (XEXP (op, 0), 0)) == SYMBOL_REF && GET_CODE (XEXP (XEXP (op, 0), 1)) == CONST_INT && INT16_P (INTVAL (XEXP (XEXP (op, 0), 1)))) - return SDATA_NAME_P (XSTR (XEXP (XEXP (op, 0), 0), 0)); + return SYMBOL_REF_SMALL_P (XEXP (XEXP (op, 0), 0)); return 0; } @@ -516,294 +505,75 @@ small_data_operand (op, int_mode) /* Return 1 if OP is a symbol that can use 24 bit addressing. */ int -addr24_operand (op, int_mode) - rtx op; - int int_mode; +addr24_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) { - if (GET_CODE (op) == LABEL_REF) - return TARGET_ADDR24; + rtx sym; - if (GET_CODE (op) == SYMBOL_REF) - return (SMALL_NAME_P (XSTR (op, 0)) - || (TARGET_ADDR24 - && (CONSTANT_POOL_ADDRESS_P (op) - || LIT_NAME_P (XSTR (op, 0))))); - - if (GET_CODE (op) == CONST - && GET_CODE (XEXP (op, 0)) == PLUS - && GET_CODE (XEXP (XEXP (op, 0), 0)) == SYMBOL_REF - && GET_CODE (XEXP (XEXP (op, 0), 1)) == CONST_INT - && UINT24_P (INTVAL (XEXP (XEXP (op, 0), 1)))) - { - rtx sym = XEXP (XEXP (op, 0), 0); - return (SMALL_NAME_P (XSTR (sym, 0)) - || (TARGET_ADDR24 - && (CONSTANT_POOL_ADDRESS_P (op) - || LIT_NAME_P (XSTR (op, 0))))); - } - - return 0; -} - -/* Return 1 if OP is a symbol that needs 32 bit addressing. */ + if (flag_pic) + return 0; -int -addr32_operand (op, int_mode) - rtx op; - int int_mode; -{ if (GET_CODE (op) == LABEL_REF) - return TARGET_ADDR32; - - if (GET_CODE (op) == SYMBOL_REF) - return (! addr24_operand (op, int_mode) - && ! small_data_operand (op, int_mode)); - - if (GET_CODE (op) == CONST - && GET_CODE (XEXP (op, 0)) == PLUS - && GET_CODE (XEXP (XEXP (op, 0), 0)) == SYMBOL_REF - && GET_CODE (XEXP (XEXP (op, 0), 1)) == CONST_INT) - { - return (! addr24_operand (op, int_mode) - && ! small_data_operand (op, int_mode)); - } - - return 0; -} - -/* Return 1 if OP is a function that can be called with the `bl' insn. */ + return TARGET_ADDR24; -int -call26_operand (op, int_mode) - rtx op; - int int_mode; -{ if (GET_CODE (op) == SYMBOL_REF) - return ! LARGE_NAME_P (XSTR (op, 0)); - - return TARGET_CALL26; -} - -/* Returns 1 if OP is an acceptable operand for seth/add3. */ + sym = op; + else if (GET_CODE (op) == CONST + && GET_CODE (XEXP (op, 0)) == PLUS + && GET_CODE (XEXP (XEXP (op, 0), 0)) == SYMBOL_REF + && GET_CODE (XEXP (XEXP (op, 0), 1)) == CONST_INT + && UINT24_P (INTVAL (XEXP (XEXP (op, 0), 1)))) + sym = XEXP (XEXP (op, 0), 0); + else + return 0; -int -seth_add3_operand (op, int_mode) - rtx op; - int int_mode; -{ - if (GET_CODE (op) == SYMBOL_REF - || GET_CODE (op) == LABEL_REF) + if (SYMBOL_REF_MODEL (sym) == M32R_MODEL_SMALL) return 1; - if (GET_CODE (op) == CONST - && GET_CODE (XEXP (op, 0)) == PLUS - && GET_CODE (XEXP (XEXP (op, 0), 0)) == SYMBOL_REF - && GET_CODE (XEXP (XEXP (op, 0), 1)) == CONST_INT - && INT16_P (INTVAL (XEXP (XEXP (op, 0), 1)))) + if (TARGET_ADDR24 + && (CONSTANT_POOL_ADDRESS_P (sym) + || LIT_NAME_P (XSTR (sym, 0)))) return 1; return 0; } -/* Return true if OP is a signed 16 bit immediate value - useful in comparisons. */ - -int -cmp_int16_operand (op, int_mode) - rtx op; - int int_mode; -{ - if (GET_CODE (op) != CONST_INT) - return 0; - return CMP_INT16_P (INTVAL (op)); -} - -/* Return true if OP is an unsigned 16 bit immediate value. */ - -int -uint16_operand (op, int_mode) - rtx op; - int int_mode; -{ - if (GET_CODE (op) != CONST_INT) - return 0; - return UINT16_P (INTVAL (op)); -} - -/* Return true if OP is a register or signed 8 bit value. */ - -int -reg_or_int16_operand (op, int_mode) - rtx op; - int int_mode; -{ - enum machine_mode mode = (enum machine_mode)int_mode; - - if (GET_CODE (op) == REG || GET_CODE (op) == SUBREG) - return register_operand (op, mode); - if (GET_CODE (op) != CONST_INT) - return 0; - return INT16_P (INTVAL (op)); -} - -/* Return true if OP is a register or an unsigned 16 bit value. */ - -int -reg_or_uint16_operand (op, int_mode) - rtx op; - int int_mode; -{ - enum machine_mode mode = (enum machine_mode)int_mode; - - if (GET_CODE (op) == REG || GET_CODE (op) == SUBREG) - return register_operand (op, mode); - if (GET_CODE (op) != CONST_INT) - return 0; - return UINT16_P (INTVAL (op)); -} - -/* Return true if OP is a register or signed 16 bit value for compares. */ +/* Return 1 if OP is a symbol that needs 32 bit addressing. */ int -reg_or_cmp_int16_operand (op, int_mode) - rtx op; - int int_mode; +addr32_operand (rtx op, enum machine_mode mode) { - enum machine_mode mode = (enum machine_mode)int_mode; - - if (GET_CODE (op) == REG || GET_CODE (op) == SUBREG) - return register_operand (op, mode); - if (GET_CODE (op) != CONST_INT) - return 0; - return CMP_INT16_P (INTVAL (op)); -} + rtx sym; -/* Return true if OP is a const_int requiring two instructions to load. */ + if (GET_CODE (op) == LABEL_REF) + return TARGET_ADDR32; -int -two_insn_const_operand (op, int_mode) - rtx op; - int int_mode; -{ - if (GET_CODE (op) != CONST_INT) - return 0; - if (INT16_P (INTVAL (op)) - || UINT24_P (INTVAL (op)) - || UPPER16_P (INTVAL (op))) + if (GET_CODE (op) == SYMBOL_REF) + sym = op; + else if (GET_CODE (op) == CONST + && GET_CODE (XEXP (op, 0)) == PLUS + && GET_CODE (XEXP (XEXP (op, 0), 0)) == SYMBOL_REF + && GET_CODE (XEXP (XEXP (op, 0), 1)) == CONST_INT + && ! flag_pic) + sym = XEXP (XEXP (op, 0), 0); + else return 0; - return 1; -} - -/* Return true if OP is an acceptable argument for a single word - move source. */ -int -move_src_operand (op, int_mode) - rtx op; - int int_mode; -{ - enum machine_mode mode = (enum machine_mode)int_mode; - switch (GET_CODE (op)) - { - case SYMBOL_REF : - case CONST : - return addr24_operand (op, int_mode); - case CONST_INT : - /* ??? We allow more cse opportunities if we only allow constants - loadable with one insn, and split the rest into two. The instances - where this would help should be rare and the current way is - simpler. */ - return INT32_P (INTVAL (op)); - case LABEL_REF : - return TARGET_ADDR24; - case CONST_DOUBLE : - if (mode == SFmode) - return 1; - else if (mode == SImode) - { - /* Large unsigned constants are represented as const_double's. */ - unsigned HOST_WIDE_INT low, high; - - low = CONST_DOUBLE_LOW (op); - high = CONST_DOUBLE_HIGH (op); - return high == 0 && low <= 0xffffffff; - } - else - return 0; - case REG : - return register_operand (op, mode); - case SUBREG : - /* (subreg (mem ...) ...) can occur here if the inner part was once a - pseudo-reg and is now a stack slot. */ - if (GET_CODE (SUBREG_REG (op)) == MEM) - return address_operand (XEXP (SUBREG_REG (op), 0), mode); - else - return register_operand (op, mode); - case MEM : - return address_operand (XEXP (op, 0), mode); - default : - return 0; - } + return (! addr24_operand (sym, mode) + && ! small_data_operand (sym, mode)); } -/* Return true if OP is an acceptable argument for a double word - move source. */ +/* Return 1 if OP is a function that can be called with the `bl' insn. */ int -move_double_src_operand (op, int_mode) - rtx op; - int int_mode; +call26_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) { - enum machine_mode mode = (enum machine_mode)int_mode; - switch (GET_CODE (op)) - { - case CONST_INT : - case CONST_DOUBLE : - return 1; - case REG : - return register_operand (op, mode); - case SUBREG : - /* (subreg (mem ...) ...) can occur here if the inner part was once a - pseudo-reg and is now a stack slot. */ - if (GET_CODE (SUBREG_REG (op)) == MEM) - return move_double_src_operand (SUBREG_REG (op), int_mode); - else - return register_operand (op, mode); - case MEM : - /* Disallow auto inc/dec for now. */ - if (GET_CODE (XEXP (op, 0)) == PRE_DEC - || GET_CODE (XEXP (op, 0)) == PRE_INC) - return 0; - return address_operand (XEXP (op, 0), mode); - default : - return 0; - } -} + if (flag_pic) + return 1; -/* Return true if OP is an acceptable argument for a move destination. */ + if (GET_CODE (op) == SYMBOL_REF) + return SYMBOL_REF_MODEL (op) != M32R_MODEL_LARGE; -int -move_dest_operand (op, int_mode) - rtx op; - int int_mode; -{ - enum machine_mode mode = (enum machine_mode)int_mode; - switch (GET_CODE (op)) - { - case REG : - return register_operand (op, mode); - case SUBREG : - /* (subreg (mem ...) ...) can occur here if the inner part was once a - pseudo-reg and is now a stack slot. */ - if (GET_CODE (SUBREG_REG (op)) == MEM) - return address_operand (XEXP (SUBREG_REG (op), 0), mode); - else - return register_operand (op, mode); - case MEM : - return address_operand (XEXP (op, 0), mode); - default : - return 0; - } + return TARGET_CALL26; } /* Return 1 if OP is a DImode const we want to handle inline. @@ -811,8 +581,7 @@ move_dest_operand (op, int_mode) It is used by the 'G' CONST_DOUBLE_OK_FOR_LETTER. */ int -easy_di_const (op) - rtx op; +easy_di_const (rtx op) { rtx high_rtx, low_rtx; HOST_WIDE_INT high, low; @@ -832,8 +601,7 @@ easy_di_const (op) It is used by the 'H' CONST_DOUBLE_OK_FOR_LETTER. */ int -easy_df_const (op) - rtx op; +easy_df_const (rtx op) { REAL_VALUE_TYPE r; long l[2]; @@ -847,108 +615,46 @@ easy_df_const (op) return 0; } -/* Return 1 if OP is an EQ or NE comparison operator. */ - -int -eqne_comparison_operator (op, int_mode) - rtx op; - int int_mode; -{ - enum rtx_code code = GET_CODE (op); - - if (GET_RTX_CLASS (code) != '<') - return 0; - return (code == EQ || code == NE); -} - -/* Return 1 if OP is a signed comparison operator. */ - -int -signed_comparison_operator (op, int_mode) - rtx op; - int int_mode; -{ - enum rtx_code code = GET_CODE (op); - - if (GET_RTX_CLASS (code) != '<') - return 0; - return (code == EQ || code == NE - || code == LT || code == LE || code == GT || code == GE); -} - /* Return 1 if OP is (mem (reg ...)). This is used in insn length calcs. */ int -memreg_operand (op, int_mode) - rtx op; - int int_mode; +memreg_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) { return GET_CODE (op) == MEM && GET_CODE (XEXP (op, 0)) == REG; } -/* Return non-zero if the operand is an insn that is a small insn. - Allow const_int 0 as well, which is a placeholder for NOP slots. */ +/* Return nonzero if TYPE must be passed by indirect reference. */ -int -small_insn_p (op, int_mode) - rtx op; - int int_mode; +static bool +m32r_pass_by_reference (CUMULATIVE_ARGS *ca ATTRIBUTE_UNUSED, + enum machine_mode mode, tree type, + bool named ATTRIBUTE_UNUSED) { - if (GET_CODE (op) == CONST_INT && INTVAL (op) == 0) - return 1; - - if (GET_RTX_CLASS (GET_CODE (op)) != 'i') - return 0; + int size; - return get_attr_length (op) == 2; -} - -/* Return non-zero if the operand is an insn that is a large insn. */ - -int -large_insn_p (op, int_mode) - rtx op; - int int_mode; -{ - if (GET_RTX_CLASS (GET_CODE (op)) != 'i') - return 0; + if (type) + size = int_size_in_bytes (type); + else + size = GET_MODE_SIZE (mode); - return get_attr_length (op) != 2; + return (size < 0 || size > 8); } - /* Comparisons. */ -/* Given a comparison code (EQ, NE, etc.) and the first operand of a COMPARE, - return the mode to be used for the comparison. */ - -int -m32r_select_cc_mode (op, x, y) - int op; - rtx x, y; -{ - return (int)SImode; -} - /* X and Y are two things to compare using CODE. Emit the compare insn and return the rtx for compare [arg0 of the if_then_else]. If need_compare is true then the comparison insn must be generated, rather - than being susummed into the following branch instruction. */ + than being subsumed into the following branch instruction. */ rtx -gen_compare (int_code, x, y, need_compare) - int int_code; - rtx x; - rtx y; - int need_compare; +gen_compare (enum rtx_code code, rtx x, rtx y, int need_compare) { - enum rtx_code code = (enum rtx_code)int_code; - enum rtx_code compare_code; - enum rtx_code branch_code; - enum machine_mode mode = SELECT_CC_MODE (code, x, y); - rtx cc_reg = gen_rtx (REG, mode, CARRY_REGNUM); - int must_swap = 0; + enum rtx_code compare_code; + enum rtx_code branch_code; + rtx cc_reg = gen_rtx_REG (CCmode, CARRY_REGNUM); + int must_swap = 0; switch (code) { @@ -962,6 +668,9 @@ gen_compare (int_code, x, y, need_compare) case LEU: compare_code = LTU; branch_code = EQ; must_swap = 1; break; case GTU: compare_code = LTU; branch_code = NE; must_swap = 1; break; case GEU: compare_code = LTU; branch_code = EQ; break; + + default: + abort (); } if (need_compare) @@ -970,27 +679,27 @@ gen_compare (int_code, x, y, need_compare) { case EQ: if (GET_CODE (y) == CONST_INT - && CMP_INT16_P (INTVAL (y)) /* reg equal to small const. */ + && CMP_INT16_P (INTVAL (y)) /* Reg equal to small const. */ && y != const0_rtx) { rtx tmp = gen_reg_rtx (SImode); - emit_insn (gen_cmp_ne_small_const_insn (tmp, x, y)); + emit_insn (gen_addsi3 (tmp, x, GEN_INT (-INTVAL (y)))); x = tmp; y = const0_rtx; } - else if (CONSTANT_P (y)) /* reg equal to const. */ + else if (CONSTANT_P (y)) /* Reg equal to const. */ { rtx tmp = force_reg (GET_MODE (x), y); y = tmp; } - if (register_operand (y, SImode) /* reg equal to reg. */ - || y == const0_rtx) /* req equal to zero. */ + if (register_operand (y, SImode) /* Reg equal to reg. */ + || y == const0_rtx) /* Reg equal to zero. */ { - emit_insn (gen_cmp_eqsi_insn (x, y)); + emit_insn (gen_cmp_eqsi_insn (x, y)); - return gen_rtx (code, mode, cc_reg, const0_rtx); + return gen_rtx_fmt_ee (code, CCmode, cc_reg, const0_rtx); } break; @@ -998,7 +707,7 @@ gen_compare (int_code, x, y, need_compare) if (register_operand (y, SImode) || (GET_CODE (y) == CONST_INT && CMP_INT16_P (INTVAL (y)))) { - rtx tmp = gen_reg_rtx (SImode); /* reg compared to reg. */ + rtx tmp = gen_reg_rtx (SImode); /* Reg compared to reg. */ switch (code) { @@ -1010,15 +719,15 @@ gen_compare (int_code, x, y, need_compare) if (y == const0_rtx) tmp = const1_rtx; else - emit_insn (gen_cmp_ne_small_const_insn (tmp, y, const1_rtx)); + emit_insn (gen_addsi3 (tmp, y, constm1_rtx)); emit_insn (gen_cmp_ltsi_insn (x, tmp)); code = EQ; break; case GT: if (GET_CODE (y) == CONST_INT) - tmp = gen_rtx (PLUS, SImode, y, const1_rtx); + tmp = gen_rtx_PLUS (SImode, y, const1_rtx); else - emit_insn (gen_cmp_ne_small_const_insn (tmp, y, const1_rtx)); + emit_insn (gen_addsi3 (tmp, y, constm1_rtx)); emit_insn (gen_cmp_ltsi_insn (x, tmp)); code = NE; break; @@ -1027,10 +736,10 @@ gen_compare (int_code, x, y, need_compare) code = NE; break; default: - abort(); + abort (); } - return gen_rtx (code, mode, cc_reg, const0_rtx); + return gen_rtx_fmt_ee (code, CCmode, cc_reg, const0_rtx); } break; @@ -1038,7 +747,7 @@ gen_compare (int_code, x, y, need_compare) if (register_operand (y, SImode) || (GET_CODE (y) == CONST_INT && CMP_INT16_P (INTVAL (y)))) { - rtx tmp = gen_reg_rtx (SImode); /* reg (unsigned) compared to reg. */ + rtx tmp = gen_reg_rtx (SImode); /* Reg (unsigned) compared to reg. */ switch (code) { @@ -1050,15 +759,15 @@ gen_compare (int_code, x, y, need_compare) if (y == const0_rtx) tmp = const1_rtx; else - emit_insn (gen_cmp_ne_small_const_insn (tmp, y, const1_rtx)); + emit_insn (gen_addsi3 (tmp, y, constm1_rtx)); emit_insn (gen_cmp_ltusi_insn (x, tmp)); code = EQ; break; case GTU: if (GET_CODE (y) == CONST_INT) - tmp = gen_rtx (PLUS, SImode, y, const1_rtx); + tmp = gen_rtx_PLUS (SImode, y, const1_rtx); else - emit_insn (gen_cmp_ne_small_const_insn (tmp, y, const1_rtx)); + emit_insn (gen_addsi3 (tmp, y, constm1_rtx)); emit_insn (gen_cmp_ltusi_insn (x, tmp)); code = NE; break; @@ -1070,7 +779,7 @@ gen_compare (int_code, x, y, need_compare) abort(); } - return gen_rtx (code, mode, cc_reg, const0_rtx); + return gen_rtx_fmt_ee (code, CCmode, cc_reg, const0_rtx); } break; @@ -1079,34 +788,35 @@ gen_compare (int_code, x, y, need_compare) } } else - if (! TARGET_OLD_COMPARE) { - /* reg/reg equal comparison */ + /* Reg/reg equal comparison. */ if (compare_code == EQ && register_operand (y, SImode)) - return gen_rtx (code, mode, x, y); + return gen_rtx_fmt_ee (code, CCmode, x, y); - /* reg/zero signed comparison */ + /* Reg/zero signed comparison. */ if ((compare_code == EQ || compare_code == LT) && y == const0_rtx) - return gen_rtx (code, mode, x, y); + return gen_rtx_fmt_ee (code, CCmode, x, y); - /* reg/smallconst equal comparison */ + /* Reg/smallconst equal comparison. */ if (compare_code == EQ && GET_CODE (y) == CONST_INT && CMP_INT16_P (INTVAL (y))) { rtx tmp = gen_reg_rtx (SImode); - emit_insn (gen_cmp_ne_small_const_insn (tmp, x, y)); - return gen_rtx (code, mode, tmp, const0_rtx); + + emit_insn (gen_addsi3 (tmp, x, GEN_INT (-INTVAL (y)))); + return gen_rtx_fmt_ee (code, CCmode, tmp, const0_rtx); } - /* reg/const equal comparison */ + /* Reg/const equal comparison. */ if (compare_code == EQ && CONSTANT_P (y)) { rtx tmp = force_reg (GET_MODE (x), y); - return gen_rtx (code, mode, x, tmp); + + return gen_rtx_fmt_ee (code, CCmode, x, tmp); } } @@ -1116,11 +826,8 @@ gen_compare (int_code, x, y, need_compare) y = force_reg (GET_MODE (x), y); else { - int ok_const = - (code == LTU || code == LEU || code == GTU || code == GEU) - ? uint16_operand (y, GET_MODE (y)) - : reg_or_cmp_int16_operand (y, GET_MODE (y)); - + int ok_const = reg_or_int16_operand (y, GET_MODE (y)); + if (! ok_const) y = force_reg (GET_MODE (x), y); } @@ -1137,16 +844,18 @@ gen_compare (int_code, x, y, need_compare) case LTU : emit_insn (gen_cmp_ltusi_insn (must_swap ? y : x, must_swap ? x : y)); break; + + default: + abort (); } - return gen_rtx (branch_code, VOIDmode, cc_reg, CONST0_RTX (mode)); + return gen_rtx_fmt_ee (branch_code, VOIDmode, cc_reg, CONST0_RTX (CCmode)); } /* Split a 2 word move (DI or DF) into component parts. */ rtx -gen_split_move_double (operands) - rtx operands[]; +gen_split_move_double (rtx operands[]) { enum machine_mode mode = GET_MODE (operands[0]); rtx dest = operands[0]; @@ -1157,16 +866,16 @@ gen_split_move_double (operands) subregs to make this code simpler. It is safe to call alter_subreg any time after reload. */ if (GET_CODE (dest) == SUBREG) - dest = alter_subreg (dest); + alter_subreg (&dest); if (GET_CODE (src) == SUBREG) - src = alter_subreg (src); + alter_subreg (&src); start_sequence (); if (GET_CODE (dest) == REG) { int dregno = REGNO (dest); - /* reg = reg */ + /* Reg = reg. */ if (GET_CODE (src) == REG) { int sregno = REGNO (src); @@ -1185,7 +894,7 @@ gen_split_move_double (operands) operand_subword (src, !reverse, TRUE, mode))); } - /* reg = constant */ + /* Reg = constant. */ else if (GET_CODE (src) == CONST_INT || GET_CODE (src) == CONST_DOUBLE) { rtx words[2]; @@ -1199,20 +908,20 @@ gen_split_move_double (operands) words[1])); } - /* reg = mem */ + /* Reg = mem. */ else if (GET_CODE (src) == MEM) { /* If the high-address word is used in the address, we must load it last. Otherwise, load it first. */ - rtx addr = XEXP (src, 0); - int reverse = (refers_to_regno_p (dregno, dregno+1, addr, 0) != 0); + int reverse + = (refers_to_regno_p (dregno, dregno + 1, XEXP (src, 0), 0) != 0); /* We used to optimize loads from single registers as ld r1,r3+; ld r2,r3 if r3 were not used subsequently. However, the REG_NOTES aren't - propigated correctly by the reload phase, and it can cause bad + propagated correctly by the reload phase, and it can cause bad code to be generated. We could still try: ld r1,r3+; ld r2,r3; addi r3,-4 @@ -1220,28 +929,25 @@ gen_split_move_double (operands) which saves 2 bytes and doesn't force longword alignment. */ emit_insn (gen_rtx_SET (VOIDmode, operand_subword (dest, reverse, TRUE, mode), - change_address (src, SImode, - plus_constant (addr, - reverse * UNITS_PER_WORD)))); + adjust_address (src, SImode, + reverse * UNITS_PER_WORD))); emit_insn (gen_rtx_SET (VOIDmode, operand_subword (dest, !reverse, TRUE, mode), - change_address (src, SImode, - plus_constant (addr, - (!reverse) * UNITS_PER_WORD)))); + adjust_address (src, SImode, + !reverse * UNITS_PER_WORD))); } - else abort (); } - /* mem = reg */ + /* Mem = reg. */ /* We used to optimize loads from single registers as st r1,r3; st r2,+r3 if r3 were not used subsequently. However, the REG_NOTES aren't - propigated correctly by the reload phase, and it can cause bad + propagated correctly by the reload phase, and it can cause bad code to be generated. We could still try: st r1,r3; st r2,+r3; addi r3,-4 @@ -1249,50 +955,51 @@ gen_split_move_double (operands) which saves 2 bytes and doesn't force longword alignment. */ else if (GET_CODE (dest) == MEM && GET_CODE (src) == REG) { - rtx addr = XEXP (dest, 0); - emit_insn (gen_rtx_SET (VOIDmode, - change_address (dest, SImode, addr), + adjust_address (dest, SImode, 0), operand_subword (src, 0, TRUE, mode))); emit_insn (gen_rtx_SET (VOIDmode, - change_address (dest, SImode, - plus_constant (addr, UNITS_PER_WORD)), + adjust_address (dest, SImode, UNITS_PER_WORD), operand_subword (src, 1, TRUE, mode))); } else abort (); - val = gen_sequence (); + val = get_insns (); end_sequence (); return val; } -/* Implements the FUNCTION_ARG_PARTIAL_NREGS macro. */ - -int -function_arg_partial_nregs (cum, int_mode, type, named) - CUMULATIVE_ARGS *cum; - int int_mode; - tree type; - int named; +static int +m32r_arg_partial_bytes (CUMULATIVE_ARGS *cum, enum machine_mode mode, + tree type, bool named ATTRIBUTE_UNUSED) { - enum machine_mode mode = (enum machine_mode)int_mode; - int ret; - int size = (((mode == BLKmode && type) - ? int_size_in_bytes (type) - : GET_MODE_SIZE (mode)) + UNITS_PER_WORD - 1) / UNITS_PER_WORD; + int words; + unsigned int size = + (((mode == BLKmode && type) + ? (unsigned int) int_size_in_bytes (type) + : GET_MODE_SIZE (mode)) + UNITS_PER_WORD - 1) + / UNITS_PER_WORD; if (*cum >= M32R_MAX_PARM_REGS) - ret = 0; + words = 0; else if (*cum + size > M32R_MAX_PARM_REGS) - ret = (*cum + size) - M32R_MAX_PARM_REGS; + words = (*cum + size) - M32R_MAX_PARM_REGS; else - ret = 0; + words = 0; - return ret; + return words * UNITS_PER_WORD; +} + +/* Worker function for TARGET_RETURN_IN_MEMORY. */ + +static bool +m32r_return_in_memory (tree type, tree fntype ATTRIBUTE_UNUSED) +{ + return m32r_pass_by_reference (NULL, TYPE_MODE (type), type, false); } /* Do any needed setup for a variadic function. For the M32R, we must @@ -1302,15 +1009,10 @@ function_arg_partial_nregs (cum, int_mode, type, named) CUM has not been updated for the last named argument which has type TYPE and mode MODE, and we rely on this fact. */ -void -m32r_setup_incoming_varargs (cum, int_mode, type, pretend_size, no_rtl) - CUMULATIVE_ARGS *cum; - int int_mode; - tree type; - int *pretend_size; - int no_rtl; +static void +m32r_setup_incoming_varargs (CUMULATIVE_ARGS *cum, enum machine_mode mode, + tree type, int *pretend_size, int no_rtl) { - enum machine_mode mode = (enum machine_mode)int_mode; int first_anon_arg; if (no_rtl) @@ -1320,12 +1022,8 @@ m32r_setup_incoming_varargs (cum, int_mode, type, pretend_size, no_rtl) if (mode == BLKmode) abort (); - /* We must treat `__builtin_va_alist' as an anonymous arg. */ - if (current_function_varargs) - first_anon_arg = *cum; - else - first_anon_arg = (ROUND_ADVANCE_CUM (*cum, mode, type) - + ROUND_ADVANCE_ARG (mode, type)); + first_anon_arg = (ROUND_ADVANCE_CUM (*cum, mode, type) + + ROUND_ADVANCE_ARG (mode, type)); if (first_anon_arg < M32R_MAX_PARM_REGS) { @@ -1335,29 +1033,102 @@ m32r_setup_incoming_varargs (cum, int_mode, type, pretend_size, no_rtl) int size = M32R_MAX_PARM_REGS - first_reg_offset; rtx regblock; - regblock = gen_rtx (MEM, BLKmode, - plus_constant (arg_pointer_rtx, - FIRST_PARM_OFFSET (0))); - move_block_from_reg (first_reg_offset, regblock, - size, size * UNITS_PER_WORD); + regblock = gen_rtx_MEM (BLKmode, + plus_constant (arg_pointer_rtx, + FIRST_PARM_OFFSET (0))); + set_mem_alias_set (regblock, get_varargs_alias_set ()); + move_block_from_reg (first_reg_offset, regblock, size); *pretend_size = (size * UNITS_PER_WORD); } } + -/* Cost functions. */ +/* Return true if INSN is real instruction bearing insn. */ -/* Provide the costs of an addressing mode that contains ADDR. - If ADDR is not a valid address, its cost is irrelevant. +static int +m32r_is_insn (rtx insn) +{ + return (INSN_P (insn) + && GET_CODE (PATTERN (insn)) != USE + && GET_CODE (PATTERN (insn)) != CLOBBER + && GET_CODE (PATTERN (insn)) != ADDR_VEC); +} - This function is trivial at the moment. This code doesn't live - in m32r.h so it's easy to experiment. */ +/* Increase the priority of long instructions so that the + short instructions are scheduled ahead of the long ones. */ -int -m32r_address_cost (addr) - rtx addr; +static int +m32r_adjust_priority (rtx insn, int priority) { - return 1; + if (m32r_is_insn (insn) + && get_attr_insn_size (insn) != INSN_SIZE_SHORT) + priority <<= 3; + + return priority; +} + + +/* Indicate how many instructions can be issued at the same time. + This is sort of a lie. The m32r can issue only 1 long insn at + once, but it can issue 2 short insns. The default therefore is + set at 2, but this can be overridden by the command line option + -missue-rate=1. */ + +static int +m32r_issue_rate (void) +{ + return ((TARGET_LOW_ISSUE_RATE) ? 1 : 2); +} + +/* Cost functions. */ + +static bool +m32r_rtx_costs (rtx x, int code, int outer_code ATTRIBUTE_UNUSED, int *total) +{ + switch (code) + { + /* Small integers are as cheap as registers. 4 byte values can be + fetched as immediate constants - let's give that the cost of an + extra insn. */ + case CONST_INT: + if (INT16_P (INTVAL (x))) + { + *total = 0; + return true; + } + /* FALLTHRU */ + + case CONST: + case LABEL_REF: + case SYMBOL_REF: + *total = COSTS_N_INSNS (1); + return true; + + case CONST_DOUBLE: + { + rtx high, low; + + split_double (x, &high, &low); + *total = COSTS_N_INSNS (!INT16_P (INTVAL (high)) + + !INT16_P (INTVAL (low))); + return true; + } + + case MULT: + *total = COSTS_N_INSNS (3); + return true; + + case DIV: + case UDIV: + case MOD: + case UMOD: + *total = COSTS_N_INSNS (10); + return true; + + default: + return false; + } } /* Type of function DECL. @@ -1366,8 +1137,7 @@ m32r_address_cost (addr) call with DECL = NULL_TREE. */ enum m32r_function_type -m32r_compute_function_type (decl) - tree decl; +m32r_compute_function_type (tree decl) { /* Cached value. */ static enum m32r_function_type fn_type = M32R_FUNCTION_UNKNOWN; @@ -1386,7 +1156,7 @@ m32r_compute_function_type (decl) return fn_type; /* Compute function type. */ - fn_type = (lookup_attribute ("interrupt", DECL_MACHINE_ATTRIBUTES (current_function_decl)) != NULL_TREE + fn_type = (lookup_attribute ("interrupt", DECL_ATTRIBUTES (current_function_decl)) != NULL_TREE ? M32R_FUNCTION_INTERRUPT : M32R_FUNCTION_NORMAL); @@ -1440,23 +1210,22 @@ Notes: containing anonymous args separately but that complicates things too much (so it's not done). 3) The return address is saved after the register save area so as to have as - many insns as possible between the restoration of `lr' and the `jmp lr'. -*/ + many insns as possible between the restoration of `lr' and the `jmp lr'. */ /* Structure to be filled in by m32r_compute_frame_size with register save masks, and offsets for the current function. */ struct m32r_frame_info { - unsigned int total_size; /* # bytes that the entire frame takes up */ - unsigned int extra_size; /* # bytes of extra stuff */ - unsigned int pretend_size; /* # bytes we push and pretend caller did */ - unsigned int args_size; /* # bytes that outgoing arguments take up */ - unsigned int reg_size; /* # bytes needed to store regs */ - unsigned int var_size; /* # bytes that variables take up */ - unsigned int gmask; /* mask of saved gp registers */ - unsigned int save_fp; /* nonzero if fp must be saved */ - unsigned int save_lr; /* nonzero if lr (return addr) must be saved */ - int initialized; /* nonzero if frame size already calculated */ + unsigned int total_size; /* # bytes that the entire frame takes up. */ + unsigned int extra_size; /* # bytes of extra stuff. */ + unsigned int pretend_size; /* # bytes we push and pretend caller did. */ + unsigned int args_size; /* # bytes that outgoing arguments take up. */ + unsigned int reg_size; /* # bytes needed to store regs. */ + unsigned int var_size; /* # bytes that variables take up. */ + unsigned int gmask; /* Mask of saved gp registers. */ + unsigned int save_fp; /* Nonzero if fp must be saved. */ + unsigned int save_lr; /* Nonzero if lr (return addr) must be saved. */ + int initialized; /* Nonzero if frame size already calculated. */ }; /* Current frame information calculated by m32r_compute_frame_size. */ @@ -1466,20 +1235,20 @@ static struct m32r_frame_info current_frame_info; static struct m32r_frame_info zero_frame_info; #define FRAME_POINTER_MASK (1 << (FRAME_POINTER_REGNUM)) -#define RETURN_ADDR_MASK (1 << (RETURN_ADDR_REGNUM)) +#define RETURN_ADDR_MASK (1 << (RETURN_ADDR_REGNUM)) /* Tell prologue and epilogue if register REGNO should be saved / restored. The return address and frame pointer are treated separately. Don't consider them here. */ #define MUST_SAVE_REGISTER(regno, interrupt_p) \ -((regno) != RETURN_ADDR_REGNUM && (regno) != FRAME_POINTER_REGNUM \ - && (regs_ever_live[regno] && (!call_used_regs[regno] || interrupt_p))) + ((regno) != RETURN_ADDR_REGNUM && (regno) != FRAME_POINTER_REGNUM \ + && (regs_ever_live[regno] && (!call_really_used_regs[regno] || interrupt_p))) #define MUST_SAVE_FRAME_POINTER (regs_ever_live[FRAME_POINTER_REGNUM]) -#define MUST_SAVE_RETURN_ADDR (regs_ever_live[RETURN_ADDR_REGNUM] || profile_flag) +#define MUST_SAVE_RETURN_ADDR (regs_ever_live[RETURN_ADDR_REGNUM] || current_function_profile) -#define SHORT_INSN_SIZE 2 /* size of small instructions */ -#define LONG_INSN_SIZE 4 /* size of long instructions */ +#define SHORT_INSN_SIZE 2 /* Size of small instructions. */ +#define LONG_INSN_SIZE 4 /* Size of long instructions. */ /* Return the bytes needed to compute the frame pointer from the current stack pointer. @@ -1487,8 +1256,7 @@ static struct m32r_frame_info zero_frame_info; SIZE is the size needed for local variables. */ unsigned int -m32r_compute_frame_size (size) - int size; /* # of var. bytes allocated. */ +m32r_compute_frame_size (int size) /* # of var. bytes allocated. */ { int regno; unsigned int total_size, var_size, args_size, pretend_size, extra_size; @@ -1496,6 +1264,7 @@ m32r_compute_frame_size (size) unsigned int gmask; enum m32r_function_type fn_type; int interrupt_p; + int pic_reg_used = flag_pic && (current_function_uses_pic_offset_table); var_size = M32R_STACK_ALIGN (size); args_size = M32R_STACK_ALIGN (current_function_outgoing_args_size); @@ -1511,10 +1280,10 @@ m32r_compute_frame_size (size) interrupt_p = M32R_INTERRUPT_P (fn_type); /* Calculate space needed for registers. */ - for (regno = 0; regno < M32R_MAX_INT_REGS; regno++) { - if (MUST_SAVE_REGISTER (regno, interrupt_p)) + if (MUST_SAVE_REGISTER (regno, interrupt_p) + || (regno == PIC_OFFSET_TABLE_REGNUM && pic_reg_used)) { reg_size += UNITS_PER_WORD; gmask |= 1 << regno; @@ -1522,7 +1291,7 @@ m32r_compute_frame_size (size) } current_frame_info.save_fp = MUST_SAVE_FRAME_POINTER; - current_frame_info.save_lr = MUST_SAVE_RETURN_ADDR; + current_frame_info.save_lr = MUST_SAVE_RETURN_ADDR || pic_reg_used; reg_size += ((current_frame_info.save_fp + current_frame_info.save_lr) * UNITS_PER_WORD); @@ -1548,27 +1317,53 @@ m32r_compute_frame_size (size) return total_size; } -/* When the `length' insn attribute is used, this macro specifies the - value to be assigned to the address of the first insn in a - function. If not specified, 0 is used. */ - -int -m32r_first_insn_address () +/* The table we use to reference PIC data. */ +static rtx global_offset_table; + +static void +m32r_reload_lr (rtx sp, int size) { - if (! current_frame_info.initialized) - m32r_compute_frame_size (get_frame_size ()); + rtx lr = gen_rtx_REG (Pmode, RETURN_ADDR_REGNUM); + + if (size == 0) + emit_insn (gen_movsi (lr, gen_rtx_MEM (Pmode, sp))); + else if (size <= 32768) + emit_insn (gen_movsi (lr, gen_rtx_MEM (Pmode, + gen_rtx_PLUS (Pmode, sp, + GEN_INT (size))))); + else + { + rtx tmp = gen_rtx_REG (Pmode, PROLOGUE_TMP_REGNUM); - return 0; + emit_insn (gen_movsi (tmp, GEN_INT (size))); + emit_insn (gen_addsi3 (tmp, tmp, sp)); + emit_insn (gen_movsi (lr, gen_rtx_MEM (Pmode, tmp))); + } + + emit_insn (gen_rtx_USE (VOIDmode, lr)); } - + +void +m32r_load_pic_register (void) +{ + global_offset_table = gen_rtx_SYMBOL_REF (Pmode, "_GLOBAL_OFFSET_TABLE_"); + emit_insn (gen_get_pc (pic_offset_table_rtx, global_offset_table, + GEN_INT (TARGET_MODEL_SMALL))); + + /* Need to emit this whether or not we obey regdecls, + since setjmp/longjmp can cause life info to screw up. */ + emit_insn (gen_rtx_USE (VOIDmode, pic_offset_table_rtx)); +} + /* Expand the m32r prologue as a series of insns. */ void -m32r_expand_prologue () +m32r_expand_prologue (void) { int regno; int frame_size; - unsigned int gmask = current_frame_info.gmask; + unsigned int gmask; + int pic_reg_used = flag_pic && (current_function_uses_pic_offset_table); if (! current_frame_info.initialized) m32r_compute_frame_size (get_frame_size ()); @@ -1591,7 +1386,6 @@ m32r_expand_prologue () } /* Save any registers we need to and set up fp. */ - if (current_frame_info.save_fp) emit_insn (gen_movsi_push (stack_pointer_rtx, frame_pointer_rtx)); @@ -1616,13 +1410,14 @@ m32r_expand_prologue () + current_frame_info.reg_size)); if (frame_size == 0) - ; /* nothing to do */ + ; /* Nothing to do. */ else if (frame_size <= 32768) emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx, GEN_INT (-frame_size))); else { rtx tmp = gen_rtx_REG (Pmode, PROLOGUE_TMP_REGNUM); + emit_insn (gen_movsi (tmp, GEN_INT (frame_size))); emit_insn (gen_subsi3 (stack_pointer_rtx, stack_pointer_rtx, tmp)); } @@ -1630,7 +1425,19 @@ m32r_expand_prologue () if (frame_pointer_needed) emit_insn (gen_movsi (frame_pointer_rtx, stack_pointer_rtx)); - if (profile_flag || profile_block_flag) + if (current_function_profile) + /* Push lr for mcount (form_pc, x). */ + emit_insn (gen_movsi_push (stack_pointer_rtx, + gen_rtx_REG (Pmode, RETURN_ADDR_REGNUM))); + + if (pic_reg_used) + { + m32r_load_pic_register (); + m32r_reload_lr (stack_pointer_rtx, + (current_function_profile ? 0 : frame_size)); + } + + if (current_function_profile && !pic_reg_used) emit_insn (gen_blockage ()); } @@ -1639,19 +1446,14 @@ m32r_expand_prologue () Note, if this is changed, you need to mirror the changes in m32r_compute_frame_size which calculates the prolog size. */ -void -m32r_output_function_prologue (file, size) - FILE * file; - int size; +static void +m32r_output_function_prologue (FILE * file, HOST_WIDE_INT size) { enum m32r_function_type fn_type = m32r_compute_function_type (current_function_decl); /* If this is an interrupt handler, mark it as such. */ if (M32R_INTERRUPT_P (fn_type)) - { - fprintf (file, "\t%s interrupt handler\n", - ASM_COMMENT_START); - } + fprintf (file, "\t%s interrupt handler\n", ASM_COMMENT_START); if (! current_frame_info.initialized) m32r_compute_frame_size (size); @@ -1667,12 +1469,10 @@ m32r_output_function_prologue (file, size) } /* Do any necessary cleanup after a function to restore stack, frame, - and regs. */ + and regs. */ -void -m32r_output_function_epilogue (file, size) - FILE * file; - int size; +static void +m32r_output_function_epilogue (FILE * file, HOST_WIDE_INT size ATTRIBUTE_UNUSED) { int regno; int noepilogue = FALSE; @@ -1700,14 +1500,12 @@ m32r_output_function_epilogue (file, size) if (!noepilogue) { - unsigned int pretend_size = current_frame_info.pretend_size; - unsigned int frame_size = total_size - pretend_size; unsigned int var_size = current_frame_info.var_size; unsigned int args_size = current_frame_info.args_size; unsigned int gmask = current_frame_info.gmask; int can_trust_sp_p = !current_function_calls_alloca; - char * sp_str = reg_names[STACK_POINTER_REGNUM]; - char * fp_str = reg_names[FRAME_POINTER_REGNUM]; + const char * sp_str = reg_names[STACK_POINTER_REGNUM]; + const char * fp_str = reg_names[FRAME_POINTER_REGNUM]; /* The first thing to do is point the sp at the bottom of the register save area. */ @@ -1715,7 +1513,7 @@ m32r_output_function_epilogue (file, size) { unsigned int reg_offset = var_size + args_size; if (reg_offset == 0) - ; /* nothing to do */ + ; /* Nothing to do. */ else if (reg_offset < 128) fprintf (file, "\taddi %s,%s%d\n", sp_str, IMMEDIATE_PREFIX, reg_offset); @@ -1731,6 +1529,7 @@ m32r_output_function_epilogue (file, size) else if (frame_pointer_needed) { unsigned int reg_offset = var_size + args_size; + if (reg_offset == 0) fprintf (file, "\tmv %s,%s\n", sp_str, fp_str); else if (reg_offset < 32768) @@ -1771,24 +1570,146 @@ m32r_output_function_epilogue (file, size) fprintf (file, "\tjmp %s\n", reg_names[RETURN_ADDR_REGNUM]); } -#if 0 /* no longer needed */ - /* Ensure the function cleanly ends on a 32 bit boundary. */ - fprintf (file, "\t.fillinsn\n"); -#endif - /* Reset state info for each function. */ current_frame_info = zero_frame_info; m32r_compute_function_type (NULL_TREE); } -/* PIC */ +/* Return nonzero if this function is known to have a null or 1 instruction + epilogue. */ + +int +direct_return (void) +{ + if (!reload_completed) + return FALSE; + + if (! current_frame_info.initialized) + m32r_compute_frame_size (get_frame_size ()); + + return current_frame_info.total_size == 0; +} + + +/* PIC. */ + +int +m32r_legitimate_pic_operand_p (rtx x) +{ + if (GET_CODE (x) == SYMBOL_REF || GET_CODE (x) == LABEL_REF) + return 0; + + if (GET_CODE (x) == CONST + && GET_CODE (XEXP (x, 0)) == PLUS + && (GET_CODE (XEXP (XEXP (x, 0), 0)) == SYMBOL_REF + || GET_CODE (XEXP (XEXP (x, 0), 0)) == LABEL_REF) + && (GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT)) + return 0; + + return 1; +} + +rtx +m32r_legitimize_pic_address (rtx orig, rtx reg) +{ +#ifdef DEBUG_PIC + printf("m32r_legitimize_pic_address()\n"); +#endif + + if (GET_CODE (orig) == SYMBOL_REF || GET_CODE (orig) == LABEL_REF) + { + rtx pic_ref, address; + rtx insn; + int subregs = 0; + + if (reg == 0) + { + if (reload_in_progress || reload_completed) + abort (); + else + reg = gen_reg_rtx (Pmode); + + subregs = 1; + } + + if (subregs) + address = gen_reg_rtx (Pmode); + else + address = reg; + + current_function_uses_pic_offset_table = 1; + + if (GET_CODE (orig) == LABEL_REF + || (GET_CODE (orig) == SYMBOL_REF && SYMBOL_REF_LOCAL_P (orig))) + { + emit_insn (gen_gotoff_load_addr (reg, orig)); + emit_insn (gen_addsi3 (reg, reg, pic_offset_table_rtx)); + return reg; + } + + emit_insn (gen_pic_load_addr (address, orig)); + + emit_insn (gen_addsi3 (address, address, pic_offset_table_rtx)); + pic_ref = gen_const_mem (Pmode, address); + insn = emit_move_insn (reg, pic_ref); +#if 0 + /* Put a REG_EQUAL note on this insn, so that it can be optimized + by loop. */ + REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_EQUAL, orig, + REG_NOTES (insn)); +#endif + return reg; + } + else if (GET_CODE (orig) == CONST) + { + rtx base, offset; + + if (GET_CODE (XEXP (orig, 0)) == PLUS + && XEXP (XEXP (orig, 0), 1) == pic_offset_table_rtx) + return orig; + + if (reg == 0) + { + if (reload_in_progress || reload_completed) + abort (); + else + reg = gen_reg_rtx (Pmode); + } + + if (GET_CODE (XEXP (orig, 0)) == PLUS) + { + base = m32r_legitimize_pic_address (XEXP (XEXP (orig, 0), 0), reg); + if (base == reg) + offset = m32r_legitimize_pic_address (XEXP (XEXP (orig, 0), 1), NULL_RTX); + else + offset = m32r_legitimize_pic_address (XEXP (XEXP (orig, 0), 1), reg); + } + else + return orig; + + if (GET_CODE (offset) == CONST_INT) + { + if (INT16_P (INTVAL (offset))) + return plus_constant (base, INTVAL (offset)); + else if (! reload_in_progress && ! reload_completed) + offset = force_reg (Pmode, offset); + else + /* If we reach here, then something is seriously wrong. */ + abort (); + } + + return gen_rtx_PLUS (Pmode, base, offset); + } + + return orig; +} /* Emit special PIC prologues and epilogues. */ void -m32r_finalize_pic () +m32r_finalize_pic (void) { - /* nothing to do */ + current_function_uses_pic_offset_table |= current_function_profile; } /* Nested function support. */ @@ -1798,21 +1719,24 @@ m32r_finalize_pic () CXT is an RTX for the static chain value for the function. */ void -m32r_initialize_trampoline (tramp, fnaddr, cxt) - rtx tramp, fnaddr, cxt; +m32r_initialize_trampoline (rtx tramp ATTRIBUTE_UNUSED, + rtx fnaddr ATTRIBUTE_UNUSED, + rtx cxt ATTRIBUTE_UNUSED) { } -/* Set the cpu type and print out other fancy things, - at the top of the file. */ - -void -m32r_asm_file_start (file) - FILE * file; +static void +m32r_file_start (void) { + default_file_start (); + if (flag_verbose_asm) - fprintf (file, "%s M32R/D special options: -G %d\n", + fprintf (asm_out_file, + "%s M32R/D special options: -G " HOST_WIDE_INT_PRINT_UNSIGNED "\n", ASM_COMMENT_START, g_switch_value); + + if (TARGET_LITTLE_ENDIAN) + fprintf (asm_out_file, "\t.little\n"); } /* Print operand X (an rtx) in assembler syntax to file FILE. @@ -1820,10 +1744,7 @@ m32r_asm_file_start (file) For `%' followed by punctuation, CODE is the punctuation and X is null. */ void -m32r_print_operand (file, x, code) - FILE * file; - rtx x; - int code; +m32r_print_operand (FILE * file, rtx x, int code) { rtx addr; @@ -1835,14 +1756,14 @@ m32r_print_operand (file, x, code) if (GET_CODE (x) == REG) fprintf (file, "@+%s", reg_names [REGNO (x)]); else - output_operand_lossage ("invalid operand to %s code"); + output_operand_lossage ("invalid operand to %%s code"); return; case 'p': if (GET_CODE (x) == REG) fprintf (file, "@%s+", reg_names [REGNO (x)]); else - output_operand_lossage ("invalid operand to %p code"); + output_operand_lossage ("invalid operand to %%p code"); return; case 'R' : @@ -1865,14 +1786,14 @@ m32r_print_operand (file, x, code) fputc (')', file); } else - output_operand_lossage ("invalid operand to %R code"); + output_operand_lossage ("invalid operand to %%R code"); return; - case 'H' : /* High word */ - case 'L' : /* Low word */ + case 'H' : /* High word. */ + case 'L' : /* Low word. */ if (GET_CODE (x) == REG) { - /* L = least significant word, H = most significant word */ + /* L = least significant word, H = most significant word. */ if ((WORDS_BIG_ENDIAN != 0) ^ (code == 'L')) fputs (reg_names[REGNO (x)], file); else @@ -1884,29 +1805,28 @@ m32r_print_operand (file, x, code) rtx first, second; split_double (x, &first, &second); - fprintf (file, "0x%08x", + fprintf (file, HOST_WIDE_INT_PRINT_HEX, code == 'L' ? INTVAL (first) : INTVAL (second)); } else - output_operand_lossage ("invalid operand to %H/%L code"); + output_operand_lossage ("invalid operand to %%H/%%L code"); return; case 'A' : { - REAL_VALUE_TYPE d; char str[30]; if (GET_CODE (x) != CONST_DOUBLE || GET_MODE_CLASS (GET_MODE (x)) != MODE_FLOAT) - abort (); - REAL_VALUE_FROM_CONST_DOUBLE (d, x); - REAL_VALUE_TO_DECIMAL (d, "%.20e", str); + fatal_insn ("bad insn for 'A'", x); + + real_to_decimal (str, CONST_DOUBLE_REAL_VALUE (x), sizeof (str), 0, 1); fprintf (file, "%s", str); return; } - case 'B' : /* Bottom half */ - case 'T' : /* Top half */ + case 'B' : /* Bottom half. */ + case 'T' : /* Top half. */ /* Output the argument to a `seth' insn (sets the Top half-word). For constants output arguments to a seth/or3 pair to set Top and Bottom halves. For symbols output arguments to a seth/add3 pair to @@ -1922,12 +1842,7 @@ m32r_print_operand (file, x, code) split_double (x, &first, &second); x = WORDS_BIG_ENDIAN ? second : first; - fprintf (file, -#if HOST_BITS_PER_WIDE_INT == HOST_BITS_PER_INT - "0x%x", -#else - "0x%lx", -#endif + fprintf (file, HOST_WIDE_INT_PRINT_HEX, (code == 'B' ? INTVAL (x) & 0xffff : (INTVAL (x) >> 16) & 0xffff)); @@ -1950,7 +1865,7 @@ m32r_print_operand (file, x, code) fputc (')', file); return; default : - output_operand_lossage ("invalid operand to %T/%B code"); + output_operand_lossage ("invalid operand to %%T/%%B code"); return; } break; @@ -1965,7 +1880,7 @@ m32r_print_operand (file, x, code) fputs (".a", file); } else - output_operand_lossage ("invalid operand to %U code"); + output_operand_lossage ("invalid operand to %%U code"); return; case 'N' : @@ -1973,31 +1888,19 @@ m32r_print_operand (file, x, code) if (GET_CODE (x) == CONST_INT) output_addr_const (file, GEN_INT (- INTVAL (x))); else - output_operand_lossage ("invalid operand to %N code"); + output_operand_lossage ("invalid operand to %%N code"); return; case 'X' : /* Print a const_int in hex. Used in comments. */ if (GET_CODE (x) == CONST_INT) - fprintf (file, -#if HOST_BITS_PER_WIDE_INT == HOST_BITS_PER_INT - "0x%x", -#else - "0x%lx", -#endif - INTVAL (x)); + fprintf (file, HOST_WIDE_INT_PRINT_HEX, INTVAL (x)); return; case '#' : fputs (IMMEDIATE_PREFIX, file); return; -#if 0 /* ??? no longer used */ - case '@' : - fputs (reg_names[SDA_REGNUM], file); - return; -#endif - case 0 : /* Do nothing special. */ break; @@ -2018,21 +1921,21 @@ m32r_print_operand (file, x, code) if (GET_CODE (addr) == PRE_INC) { if (GET_CODE (XEXP (addr, 0)) != REG) - abort (); + fatal_insn ("pre-increment address is not a register", x); fprintf (file, "@+%s", reg_names[REGNO (XEXP (addr, 0))]); } else if (GET_CODE (addr) == PRE_DEC) { if (GET_CODE (XEXP (addr, 0)) != REG) - abort (); + fatal_insn ("pre-decrement address is not a register", x); fprintf (file, "@-%s", reg_names[REGNO (XEXP (addr, 0))]); } else if (GET_CODE (addr) == POST_INC) { if (GET_CODE (XEXP (addr, 0)) != REG) - abort (); + fatal_insn ("post-increment address is not a register", x); fprintf (file, "@%s+", reg_names[REGNO (XEXP (addr, 0))]); } @@ -2068,13 +1971,11 @@ m32r_print_operand (file, x, code) /* Print a memory address as an operand to reference that memory location. */ void -m32r_print_operand_address (file, addr) - FILE * file; - rtx addr; +m32r_print_operand_address (FILE * file, rtx addr) { - register rtx base; - register rtx index = 0; - int offset = 0; + rtx base; + rtx index = 0; + int offset = 0; switch (GET_CODE (addr)) { @@ -2110,7 +2011,7 @@ m32r_print_operand_address (file, addr) fputs (reg_names[REGNO (base)], file); } else - abort (); + fatal_insn ("bad address", addr); } else if (GET_CODE (base) == LO_SUM) { @@ -2126,12 +2027,12 @@ m32r_print_operand_address (file, addr) fputs (reg_names[REGNO (XEXP (base, 0))], file); } else - abort (); + fatal_insn ("bad address", addr); break; case LO_SUM : if (GET_CODE (XEXP (addr, 0)) != REG) - abort (); + fatal_insn ("lo_sum not of register", addr); if (small_data_operand (XEXP (addr, 1), VOIDmode)) fputs ("sda(", file); else @@ -2141,15 +2042,15 @@ m32r_print_operand_address (file, addr) fputs (reg_names[REGNO (XEXP (addr, 0))], file); break; - case PRE_INC : /* Assume SImode */ + case PRE_INC : /* Assume SImode. */ fprintf (file, "+%s", reg_names[REGNO (XEXP (addr, 0))]); break; - case PRE_DEC : /* Assume SImode */ + case PRE_DEC : /* Assume SImode. */ fprintf (file, "-%s", reg_names[REGNO (XEXP (addr, 0))]); break; - case POST_INC : /* Assume SImode */ + case POST_INC : /* Assume SImode. */ fprintf (file, "%s+", reg_names[REGNO (XEXP (addr, 0))]); break; @@ -2160,10 +2061,9 @@ m32r_print_operand_address (file, addr) } /* Return true if the operands are the constants 0 and 1. */ + int -zero_and_one (operand1, operand2) - rtx operand1; - rtx operand2; +zero_and_one (rtx operand1, rtx operand2) { return GET_CODE (operand1) == CONST_INT @@ -2172,76 +2072,17 @@ zero_and_one (operand1, operand2) ||((INTVAL (operand1) == 1) && (INTVAL (operand2) == 0))); } -/* Return non-zero if the operand is suitable for use in a conditional move sequence. */ -int -conditional_move_operand (operand, int_mode) - rtx operand; - int int_mode; -{ - enum machine_mode mode = (enum machine_mode)int_mode; - - /* Only defined for simple integers so far... */ - if (mode != SImode && mode != HImode && mode != QImode) - return FALSE; - - /* At the moment we can hanndle moving registers and loading constants. */ - /* To be added: Addition/subtraction/bitops/multiplication of registers. */ - - switch (GET_CODE (operand)) - { - case REG: - return 1; - - case CONST_INT: - return INT8_P (INTVAL (operand)); - - default: -#if 0 - fprintf (stderr, "Test for cond move op of type: %s\n", - GET_RTX_NAME (GET_CODE (operand))); -#endif - return 0; - } -} - -/* Return true if the code is a test of the carry bit */ -int -carry_compare_operand (op, int_mode) - rtx op; - int int_mode; -{ - rtx x; - - if (GET_MODE (op) != SImode && GET_MODE (op) != VOIDmode) - return FALSE; - - if (GET_CODE (op) != NE && GET_CODE (op) != EQ) - return FALSE; - - x = XEXP (op, 0); - if (GET_CODE (x) != REG || REGNO (x) != CARRY_REGNUM) - return FALSE; - - x = XEXP (op, 1); - if (GET_CODE (x) != CONST_INT || INTVAL (x) != 0) - return FALSE; - - return TRUE; -} - - /* Generate the correct assembler code to handle the conditional loading of a value into a register. It is known that the operands satisfy the conditional_move_operand() function above. The destination is operand[0]. The condition is operand [1]. The 'true' value is operand [2] and the 'false' value is operand [3]. */ + char * -emit_cond_move (operands, insn) - rtx * operands; - rtx insn; +emit_cond_move (rtx * operands, rtx insn ATTRIBUTE_UNUSED) { static char buffer [100]; - char * dest = reg_names [REGNO (operands [0])]; + const char * dest = reg_names [REGNO (operands [0])]; buffer [0] = 0; @@ -2262,23 +2103,67 @@ emit_cond_move (operands, insn) } sprintf (buffer, "mvfc %s, cbr", dest); - + /* If the true value was '0' then we need to invert the results of the move. */ if (INTVAL (operands [2]) == 0) sprintf (buffer + strlen (buffer), "\n\txor3 %s, %s, #1", dest, dest); - + return buffer; } +/* Returns true if the registers contained in the two + rtl expressions are different. */ + +int +m32r_not_same_reg (rtx a, rtx b) +{ + int reg_a = -1; + int reg_b = -2; + + while (GET_CODE (a) == SUBREG) + a = SUBREG_REG (a); + + if (GET_CODE (a) == REG) + reg_a = REGNO (a); + + while (GET_CODE (b) == SUBREG) + b = SUBREG_REG (b); + + if (GET_CODE (b) == REG) + reg_b = REGNO (b); + + return reg_a != reg_b; +} +rtx +m32r_function_symbol (const char *name) +{ + int extra_flags = 0; + enum m32r_model model; + rtx sym = gen_rtx_SYMBOL_REF (Pmode, name); + + if (TARGET_MODEL_SMALL) + model = M32R_MODEL_SMALL; + else if (TARGET_MODEL_MEDIUM) + model = M32R_MODEL_MEDIUM; + else if (TARGET_MODEL_LARGE) + model = M32R_MODEL_LARGE; + else + abort (); /* Shouldn't happen. */ + extra_flags |= model << SYMBOL_FLAG_MODEL_SHIFT; + + if (extra_flags) + SYMBOL_REF_FLAGS (sym) |= extra_flags; + + return sym; +} + /* Use a library function to move some bytes. */ + static void -block_move_call (dest_reg, src_reg, bytes_rtx) - rtx dest_reg; - rtx src_reg; - rtx bytes_rtx; +block_move_call (rtx dest_reg, rtx src_reg, rtx bytes_rtx) { /* We want to pass the size as Pmode, which will normally be SImode but will be DImode if we are using 64 bit longs and pointers. */ @@ -2286,28 +2171,13 @@ block_move_call (dest_reg, src_reg, bytes_rtx) && GET_MODE (bytes_rtx) != Pmode) bytes_rtx = convert_to_mode (Pmode, bytes_rtx, 1); -#ifdef TARGET_MEM_FUNCTIONS - emit_library_call (gen_rtx (SYMBOL_REF, Pmode, "memcpy"), 0, + emit_library_call (m32r_function_symbol ("memcpy"), 0, VOIDmode, 3, dest_reg, Pmode, src_reg, Pmode, convert_to_mode (TYPE_MODE (sizetype), bytes_rtx, - TREE_UNSIGNED (sizetype)), + TYPE_UNSIGNED (sizetype)), TYPE_MODE (sizetype)); -#else - emit_library_call (gen_rtx (SYMBOL_REF, Pmode, "bcopy"), 0, - VOIDmode, 3, src_reg, Pmode, dest_reg, Pmode, - convert_to_mode (TYPE_MODE (integer_type_node), bytes_rtx, - TREE_UNSIGNED (integer_type_node)), - TYPE_MODE (integer_type_node)); -#endif } -/* The maximum number of bytes to copy using pairs of load/store instructions. - If a block is larger than this then a loop will be generated to copy - MAX_MOVE_BYTES chunks at a time. The value of 32 is a semi-arbitary choice. - A customer uses Dhrystome as their benchmark, and Dhrystone has a 31 byte - string copy in it. */ -#define MAX_MOVE_BYTES 32 - /* Expand string/block move operations. operands[0] is the pointer to the destination. @@ -2316,8 +2186,7 @@ block_move_call (dest_reg, src_reg, bytes_rtx) operands[3] is the alignment. */ void -m32r_expand_block_move (operands) - rtx operands[]; +m32r_expand_block_move (rtx operands[]) { rtx orig_dst = operands[0]; rtx orig_src = operands[1]; @@ -2355,15 +2224,17 @@ m32r_expand_block_move (operands) /* If necessary, generate a loop to handle the bulk of the copy. */ if (bytes) { - rtx label; - rtx final_src; + rtx label = NULL_RTX; + rtx final_src = NULL_RTX; rtx at_a_time = GEN_INT (MAX_MOVE_BYTES); rtx rounded_total = GEN_INT (bytes); + rtx new_dst_reg = gen_reg_rtx (SImode); + rtx new_src_reg = gen_reg_rtx (SImode); /* If we are going to have to perform this loop more than once, then generate a label and compute the address the source register will contain upon completion of the final - itteration. */ + iteration. */ if (bytes > MAX_MOVE_BYTES) { final_src = gen_reg_rtx (Pmode); @@ -2384,7 +2255,10 @@ m32r_expand_block_move (operands) to the word after the end of the source block, and dst_reg to point to the last word of the destination block, provided that the block is MAX_MOVE_BYTES long. */ - emit_insn (gen_movstrsi_internal (dst_reg, src_reg, at_a_time)); + emit_insn (gen_movmemsi_internal (dst_reg, src_reg, at_a_time, + new_dst_reg, new_src_reg)); + emit_move_insn (dst_reg, new_dst_reg); + emit_move_insn (src_reg, new_src_reg); emit_insn (gen_addsi3 (dst_reg, dst_reg, GEN_INT (4))); if (bytes > MAX_MOVE_BYTES) @@ -2395,7 +2269,9 @@ m32r_expand_block_move (operands) } if (leftover) - emit_insn (gen_movstrsi_internal (dst_reg, src_reg, GEN_INT (leftover))); + emit_insn (gen_movmemsi_internal (dst_reg, src_reg, GEN_INT (leftover), + gen_reg_rtx (SImode), + gen_reg_rtx (SImode))); } @@ -2407,10 +2283,8 @@ m32r_expand_block_move (operands) operands[3] is a temp register. operands[4] is a temp register. */ -char * -m32r_output_block_move (insn, operands) - rtx insn; - rtx operands[]; +void +m32r_output_block_move (rtx insn ATTRIBUTE_UNUSED, rtx operands[]) { HOST_WIDE_INT bytes = INTVAL (operands[2]); int first_time; @@ -2423,7 +2297,7 @@ m32r_output_block_move (insn, operands) stores are done without any increment, then the remaining ones can use the pre-increment addressing mode. - Note: expand_block_move() also relies upon this behaviour when building + Note: expand_block_move() also relies upon this behavior when building loops to copy large blocks. */ first_time = 1; @@ -2433,17 +2307,17 @@ m32r_output_block_move (insn, operands) { if (first_time) { - output_asm_insn ("ld\t%3, %p1", operands); - output_asm_insn ("ld\t%4, %p1", operands); - output_asm_insn ("st\t%3, @%0", operands); - output_asm_insn ("st\t%4, %s0", operands); + output_asm_insn ("ld\t%5, %p1", operands); + output_asm_insn ("ld\t%6, %p1", operands); + output_asm_insn ("st\t%5, @%0", operands); + output_asm_insn ("st\t%6, %s0", operands); } else { - output_asm_insn ("ld\t%3, %p1", operands); - output_asm_insn ("ld\t%4, %p1", operands); - output_asm_insn ("st\t%3, %s0", operands); - output_asm_insn ("st\t%4, %s0", operands); + output_asm_insn ("ld\t%5, %p1", operands); + output_asm_insn ("ld\t%6, %p1", operands); + output_asm_insn ("st\t%5, %s0", operands); + output_asm_insn ("st\t%6, %s0", operands); } bytes -= 8; @@ -2453,15 +2327,15 @@ m32r_output_block_move (insn, operands) if (bytes > 4) got_extra = 1; - output_asm_insn ("ld\t%3, %p1", operands); + output_asm_insn ("ld\t%5, %p1", operands); if (got_extra) - output_asm_insn ("ld\t%4, %p1", operands); + output_asm_insn ("ld\t%6, %p1", operands); if (first_time) - output_asm_insn ("st\t%3, @%0", operands); + output_asm_insn ("st\t%5, @%0", operands); else - output_asm_insn ("st\t%3, %s0", operands); + output_asm_insn ("st\t%5, %s0", operands); bytes -= 4; } @@ -2473,20 +2347,25 @@ m32r_output_block_move (insn, operands) valid memory [since we don't get called if things aren't properly aligned]. */ int dst_offset = first_time ? 0 : 4; + /* The amount of increment we have to make to the + destination pointer. */ + int dst_inc_amount = dst_offset + bytes - 4; + /* The same for the source pointer. */ + int src_inc_amount = bytes; int last_shift; rtx my_operands[3]; /* If got_extra is true then we have already loaded the next word as part of loading and storing the previous word. */ if (! got_extra) - output_asm_insn ("ld\t%4, @%1", operands); + output_asm_insn ("ld\t%6, @%1", operands); if (bytes >= 2) { bytes -= 2; - output_asm_insn ("sra3\t%3, %4, #16", operands); - my_operands[0] = operands[3]; + output_asm_insn ("sra3\t%5, %6, #16", operands); + my_operands[0] = operands[5]; my_operands[1] = GEN_INT (dst_offset); my_operands[2] = operands[0]; output_asm_insn ("sth\t%0, @(%1,%2)", my_operands); @@ -2507,35 +2386,68 @@ m32r_output_block_move (insn, operands) if (bytes > 0) { - my_operands[0] = operands[4]; + my_operands[0] = operands[6]; my_operands[1] = GEN_INT (last_shift); output_asm_insn ("srai\t%0, #%1", my_operands); - my_operands[0] = operands[4]; + my_operands[0] = operands[6]; my_operands[1] = GEN_INT (dst_offset); my_operands[2] = operands[0]; output_asm_insn ("stb\t%0, @(%1,%2)", my_operands); } + + /* Update the destination pointer if needed. We have to do + this so that the patterns matches what we output in this + function. */ + if (dst_inc_amount + && !find_reg_note (insn, REG_UNUSED, operands[0])) + { + my_operands[0] = operands[0]; + my_operands[1] = GEN_INT (dst_inc_amount); + output_asm_insn ("addi\t%0, #%1", my_operands); + } + + /* Update the source pointer if needed. We have to do this + so that the patterns matches what we output in this + function. */ + if (src_inc_amount + && !find_reg_note (insn, REG_UNUSED, operands[1])) + { + my_operands[0] = operands[1]; + my_operands[1] = GEN_INT (src_inc_amount); + output_asm_insn ("addi\t%0, #%1", my_operands); + } bytes = 0; } first_time = 0; } - - return ""; } -/* Return true if op is an integer constant, less than or equal to - MAX_MOVE_BYTES. */ +/* Return true if using NEW_REG in place of OLD_REG is ok. */ + int -m32r_block_immediate_operand (op, mode) - rtx op; - int mode; +m32r_hard_regno_rename_ok (unsigned int old_reg ATTRIBUTE_UNUSED, + unsigned int new_reg) { - if (GET_CODE (op) != CONST_INT - || INTVAL (op) > MAX_MOVE_BYTES - || INTVAL (op) <= 0) + /* Interrupt routines can't clobber any register that isn't already used. */ + if (lookup_attribute ("interrupt", DECL_ATTRIBUTES (current_function_decl)) + && !regs_ever_live[new_reg]) + return 0; + + /* We currently emit epilogues as text, not rtl, so the liveness + of the return address register isn't visible. */ + if (current_function_is_leaf && new_reg == RETURN_ADDR_REGNUM) return 0; return 1; } + +rtx +m32r_return_addr (int count) +{ + if (count != 0) + return const0_rtx; + + return get_hard_reg_initial_val (Pmode, RETURN_ADDR_REGNUM); +}