X-Git-Url: http://git.sourceforge.jp/view?a=blobdiff_plain;f=gcc%2Fconfig%2Fmn10300%2Fmn10300.c;h=b55ca37c00c92e89ef546b20404811a7f19ea244;hb=257d99c3322b212eaf262e0078549956d90c2141;hp=2c68dcb04bab7bb52ea9bbd19352fe18902389c5;hpb=10ec06d3bc25ab47e61bcfd7126fb4f695f660ca;p=pf3gnuchains%2Fgcc-fork.git diff --git a/gcc/config/mn10300/mn10300.c b/gcc/config/mn10300/mn10300.c index 2c68dcb04ba..b55ca37c00c 100644 --- a/gcc/config/mn10300/mn10300.c +++ b/gcc/config/mn10300/mn10300.c @@ -1,13 +1,13 @@ /* Subroutines for insn-output.c for Matsushita MN10300 series - Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004 - Free Software Foundation, Inc. + Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, + 2005, 2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc. Contributed by Jeff Law (law@cygnus.com). 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, @@ -16,9 +16,8 @@ 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" @@ -28,17 +27,18 @@ Boston, MA 02111-1307, USA. */ #include "tree.h" #include "regs.h" #include "hard-reg-set.h" -#include "real.h" #include "insn-config.h" #include "conditions.h" #include "output.h" #include "insn-attr.h" #include "flags.h" #include "recog.h" +#include "reload.h" #include "expr.h" #include "optabs.h" #include "function.h" #include "obstack.h" +#include "diagnostic-core.h" #include "toplev.h" #include "tm_p.h" #include "target.h" @@ -54,29 +54,50 @@ int mn10300_unspec_int_label_counter; symbol names from register names. */ int mn10300_protect_label; +/* The selected processor. */ +enum processor_type mn10300_processor = PROCESSOR_DEFAULT; + /* The size of the callee register save area. Right now we save everything on entry since it costs us nothing in code size. It does cost us from a speed standpoint, so we want to optimize this sooner or later. */ -#define REG_SAVE_BYTES (4 * regs_ever_live[2] \ - + 4 * regs_ever_live[3] \ - + 4 * regs_ever_live[6] \ - + 4 * regs_ever_live[7] \ - + 16 * (regs_ever_live[14] || regs_ever_live[15] \ - || regs_ever_live[16] || regs_ever_live[17])) +#define REG_SAVE_BYTES (4 * df_regs_ever_live_p (2) \ + + 4 * df_regs_ever_live_p (3) \ + + 4 * df_regs_ever_live_p (6) \ + + 4 * df_regs_ever_live_p (7) \ + + 16 * (df_regs_ever_live_p (14) || df_regs_ever_live_p (15) \ + || df_regs_ever_live_p (16) || df_regs_ever_live_p (17))) +static bool mn10300_handle_option (size_t, const char *, int); +static bool mn10300_legitimate_address_p (enum machine_mode, rtx, bool); static int mn10300_address_cost_1 (rtx, int *); -static int mn10300_address_cost (rtx); -static bool mn10300_rtx_costs (rtx, int, int, int *); +static int mn10300_address_cost (rtx, bool); +static bool mn10300_rtx_costs (rtx, int, int, int *, bool); static void mn10300_file_start (void); -static bool mn10300_return_in_memory (tree, tree); +static bool mn10300_return_in_memory (const_tree, const_tree); static rtx mn10300_builtin_saveregs (void); - +static void mn10300_va_start (tree, rtx); +static rtx mn10300_legitimize_address (rtx, rtx, enum machine_mode); +static bool mn10300_pass_by_reference (CUMULATIVE_ARGS *, enum machine_mode, + const_tree, bool); +static int mn10300_arg_partial_bytes (CUMULATIVE_ARGS *, enum machine_mode, + tree, bool); +static unsigned int mn10300_case_values_threshold (void); +static void mn10300_encode_section_info (tree, rtx, int); +static void mn10300_asm_trampoline_template (FILE *); +static void mn10300_trampoline_init (rtx, tree, rtx); +static rtx mn10300_function_value (const_tree, const_tree, bool); +static rtx mn10300_libcall_value (enum machine_mode, const_rtx); +static void mn10300_asm_output_mi_thunk (FILE *, tree, HOST_WIDE_INT, HOST_WIDE_INT, tree); +static bool mn10300_can_output_mi_thunk (const_tree, HOST_WIDE_INT, HOST_WIDE_INT, const_tree); /* Initialize the GCC target structure. */ #undef TARGET_ASM_ALIGNED_HI_OP #define TARGET_ASM_ALIGNED_HI_OP "\t.hword\t" +#undef TARGET_LEGITIMIZE_ADDRESS +#define TARGET_LEGITIMIZE_ADDRESS mn10300_legitimize_address + #undef TARGET_RTX_COSTS #define TARGET_RTX_COSTS mn10300_rtx_costs #undef TARGET_ADDRESS_COST @@ -87,21 +108,84 @@ static rtx mn10300_builtin_saveregs (void); #undef TARGET_ASM_FILE_START_FILE_DIRECTIVE #define TARGET_ASM_FILE_START_FILE_DIRECTIVE true +#undef TARGET_DEFAULT_TARGET_FLAGS +#define TARGET_DEFAULT_TARGET_FLAGS MASK_MULT_BUG | MASK_PTR_A0D0 +#undef TARGET_HANDLE_OPTION +#define TARGET_HANDLE_OPTION mn10300_handle_option + #undef TARGET_ENCODE_SECTION_INFO #define TARGET_ENCODE_SECTION_INFO mn10300_encode_section_info #undef TARGET_PROMOTE_PROTOTYPES -#define TARGET_PROMOTE_PROTOTYPES hook_bool_tree_true - +#define TARGET_PROMOTE_PROTOTYPES hook_bool_const_tree_true #undef TARGET_RETURN_IN_MEMORY #define TARGET_RETURN_IN_MEMORY mn10300_return_in_memory +#undef TARGET_PASS_BY_REFERENCE +#define TARGET_PASS_BY_REFERENCE mn10300_pass_by_reference +#undef TARGET_CALLEE_COPIES +#define TARGET_CALLEE_COPIES hook_bool_CUMULATIVE_ARGS_mode_tree_bool_true +#undef TARGET_ARG_PARTIAL_BYTES +#define TARGET_ARG_PARTIAL_BYTES mn10300_arg_partial_bytes #undef TARGET_EXPAND_BUILTIN_SAVEREGS #define TARGET_EXPAND_BUILTIN_SAVEREGS mn10300_builtin_saveregs +#undef TARGET_EXPAND_BUILTIN_VA_START +#define TARGET_EXPAND_BUILTIN_VA_START mn10300_va_start + +#undef TARGET_CASE_VALUES_THRESHOLD +#define TARGET_CASE_VALUES_THRESHOLD mn10300_case_values_threshold + +#undef TARGET_LEGITIMATE_ADDRESS_P +#define TARGET_LEGITIMATE_ADDRESS_P mn10300_legitimate_address_p + +#undef TARGET_ASM_TRAMPOLINE_TEMPLATE +#define TARGET_ASM_TRAMPOLINE_TEMPLATE mn10300_asm_trampoline_template +#undef TARGET_TRAMPOLINE_INIT +#define TARGET_TRAMPOLINE_INIT mn10300_trampoline_init + +#undef TARGET_FUNCTION_VALUE +#define TARGET_FUNCTION_VALUE mn10300_function_value +#undef TARGET_LIBCALL_VALUE +#define TARGET_LIBCALL_VALUE mn10300_libcall_value + +#undef TARGET_ASM_OUTPUT_MI_THUNK +#define TARGET_ASM_OUTPUT_MI_THUNK mn10300_asm_output_mi_thunk +#undef TARGET_ASM_CAN_OUTPUT_MI_THUNK +#define TARGET_ASM_CAN_OUTPUT_MI_THUNK mn10300_can_output_mi_thunk -static void mn10300_encode_section_info (tree, rtx, int); struct gcc_target targetm = TARGET_INITIALIZER; +/* Implement TARGET_HANDLE_OPTION. */ + +static bool +mn10300_handle_option (size_t code, + const char *arg ATTRIBUTE_UNUSED, + int value) +{ + switch (code) + { + case OPT_mam33: + mn10300_processor = value ? PROCESSOR_AM33 : PROCESSOR_MN10300; + return true; + case OPT_mam33_2: + mn10300_processor = (value + ? PROCESSOR_AM33_2 + : MIN (PROCESSOR_AM33, PROCESSOR_DEFAULT)); + return true; + default: + return true; + } +} + +/* Implement OVERRIDE_OPTIONS. */ + +void +mn10300_override_options (void) +{ + if (TARGET_AM33) + target_flags &= ~MASK_MULT_BUG; +} + static void mn10300_file_start (void) { @@ -172,7 +256,7 @@ print_operand (FILE *file, rtx x, int code) fprintf (file, "ul"); break; default: - abort (); + gcc_unreachable (); } break; } @@ -210,7 +294,7 @@ print_operand (FILE *file, rtx x, int code) fprintf (file, "cs"); break; default: - abort (); + gcc_unreachable (); } break; case 'C': @@ -226,7 +310,7 @@ print_operand (FILE *file, rtx x, int code) else print_operand (file, x, 0); break; - + case 'D': switch (GET_CODE (x)) { @@ -241,7 +325,7 @@ print_operand (FILE *file, rtx x, int code) break; default: - abort (); + gcc_unreachable (); } break; @@ -300,7 +384,7 @@ print_operand (FILE *file, rtx x, int code) } default: - abort (); + gcc_unreachable (); } break; @@ -336,10 +420,10 @@ print_operand (FILE *file, rtx x, int code) fprintf (file, "0x%lx", val[1]); break;; case SFmode: - abort (); + gcc_unreachable (); case VOIDmode: case DImode: - print_operand_address (file, + print_operand_address (file, GEN_INT (CONST_DOUBLE_HIGH (x))); break; default: @@ -357,7 +441,7 @@ print_operand (FILE *file, rtx x, int code) } default: - abort (); + gcc_unreachable (); } break; @@ -371,14 +455,12 @@ print_operand (FILE *file, rtx x, int code) break; case 'N': - if (INTVAL (x) < -128 || INTVAL (x) > 255) - abort (); + gcc_assert (INTVAL (x) >= -128 && INTVAL (x) <= 255); fprintf (file, "%d", (int)((~INTVAL (x)) & 0xff)); break; case 'U': - if (INTVAL (x) < -128 || INTVAL (x) > 255) - abort (); + gcc_assert (INTVAL (x) >= -128 && INTVAL (x) <= 255); fprintf (file, "%d", (int)(INTVAL (x) & 0xff)); break; @@ -436,7 +518,7 @@ print_operand (FILE *file, rtx x, int code) print_operand_address (file, x); break; default: - abort (); + gcc_unreachable (); } break; } @@ -466,7 +548,7 @@ print_operand_address (FILE *file, rtx addr) && REG_OK_FOR_BASE_P (XEXP (addr, 1))) base = XEXP (addr, 1), index = XEXP (addr, 0); else - abort (); + gcc_unreachable (); print_operand (file, index, 0); fputc (',', file); print_operand (file, base, 0);; @@ -491,7 +573,7 @@ fp_regs_to_save (void) return 0; for (i = FIRST_FP_REGNUM; i <= LAST_FP_REGNUM; ++i) - if (regs_ever_live[i] && ! call_used_regs[i]) + if (df_regs_ever_live_p (i) && ! call_really_used_regs[i]) ++n; return n; @@ -500,7 +582,7 @@ fp_regs_to_save (void) /* Print a set of registers in the format required by "movm" and "ret". Register K is saved if bit K of MASK is set. The data and address registers can be stored individually, but the extended registers cannot. - We assume that the mask alread takes that into account. For instance, + We assume that the mask already takes that into account. For instance, bits 14 to 17 must have the same value. */ void @@ -523,8 +605,7 @@ mn10300_print_reg_list (FILE *file, int mask) if ((mask & 0x3c000) != 0) { - if ((mask & 0x3c000) != 0x3c000) - abort(); + gcc_assert ((mask & 0x3c000) == 0x3c000); if (need_comma) fputc (',', file); fputs ("exreg1", file); @@ -538,21 +619,21 @@ int can_use_return_insn (void) { /* size includes the fixed stack space needed for function calls. */ - int size = get_frame_size () + current_function_outgoing_args_size; + int size = get_frame_size () + crtl->outgoing_args_size; /* And space for the return pointer. */ - size += current_function_outgoing_args_size ? 4 : 0; + size += crtl->outgoing_args_size ? 4 : 0; return (reload_completed && size == 0 - && !regs_ever_live[2] - && !regs_ever_live[3] - && !regs_ever_live[6] - && !regs_ever_live[7] - && !regs_ever_live[14] - && !regs_ever_live[15] - && !regs_ever_live[16] - && !regs_ever_live[17] + && !df_regs_ever_live_p (2) + && !df_regs_ever_live_p (3) + && !df_regs_ever_live_p (6) + && !df_regs_ever_live_p (7) + && !df_regs_ever_live_p (14) + && !df_regs_ever_live_p (15) + && !df_regs_ever_live_p (16) + && !df_regs_ever_live_p (17) && fp_regs_to_save () == 0 && !frame_pointer_needed); } @@ -569,7 +650,7 @@ mn10300_get_live_callee_saved_regs (void) mask = 0; for (i = 0; i <= LAST_EXTENDED_REGNUM; i++) - if (regs_ever_live[i] && ! call_used_regs[i]) + if (df_regs_ever_live_p (i) && ! call_really_used_regs[i]) mask |= (1 << i); if ((mask & 0x3c000) != 0) mask |= 0x3c000; @@ -650,8 +731,8 @@ expand_prologue (void) HOST_WIDE_INT size; /* SIZE includes the fixed stack space needed for function calls. */ - size = get_frame_size () + current_function_outgoing_args_size; - size += (current_function_outgoing_args_size ? 4 : 0); + size = get_frame_size () + crtl->outgoing_args_size; + size += (crtl->outgoing_args_size ? 4 : 0); /* If we use any of the callee-saved registers, save them now. */ mn10300_gen_multiple_store (mn10300_get_live_callee_saved_regs ()); @@ -756,7 +837,7 @@ expand_prologue (void) frame pointer, size is nonzero and the user hasn't changed the calling conventions of a0. */ if (! frame_pointer_needed && size - && call_used_regs[FIRST_ADDRESS_REGNUM] + && call_really_used_regs [FIRST_ADDRESS_REGNUM] && ! fixed_regs[FIRST_ADDRESS_REGNUM]) { /* Insn: add -(size + 4 * num_regs_to_save), sp. */ @@ -780,7 +861,7 @@ expand_prologue (void) /* Consider alternative save_a0_no_merge if the user hasn't changed the calling conventions of a0. */ - if (call_used_regs[FIRST_ADDRESS_REGNUM] + if (call_really_used_regs [FIRST_ADDRESS_REGNUM] && ! fixed_regs[FIRST_ADDRESS_REGNUM]) { /* Insn: add -4 * num_regs_to_save, sp. */ @@ -835,9 +916,9 @@ expand_prologue (void) break; default: - abort (); + gcc_unreachable (); } - + /* Now prepare register a0, if we have decided to use it. */ switch (strategy) { @@ -855,14 +936,14 @@ expand_prologue (void) emit_insn (gen_addsi3 (reg, reg, GEN_INT (xsize))); reg = gen_rtx_POST_INC (SImode, reg); break; - + default: - abort (); + gcc_unreachable (); } - + /* Now actually save the FP registers. */ for (i = FIRST_FP_REGNUM; i <= LAST_FP_REGNUM; ++i) - if (regs_ever_live[i] && ! call_used_regs[i]) + if (df_regs_ever_live_p (i) && ! call_really_used_regs [i]) { rtx addr; @@ -879,7 +960,7 @@ expand_prologue (void) } else addr = stack_pointer_rtx; - + xsize += 4; } @@ -899,24 +980,8 @@ expand_prologue (void) emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx, GEN_INT (-size))); - if (flag_pic && regs_ever_live[PIC_OFFSET_TABLE_REGNUM]) - { - rtx insn = get_last_insn (); - rtx last = emit_insn (gen_GOTaddr2picreg ()); - - /* Mark these insns as possibly dead. Sometimes, flow2 may - delete all uses of the PIC register. In this case, let it - delete the initialization too. */ - do - { - insn = NEXT_INSN (insn); - - REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD, - const0_rtx, - REG_NOTES (insn)); - } - while (insn != last); - } + if (flag_pic && df_regs_ever_live_p (PIC_OFFSET_TABLE_REGNUM)) + emit_insn (gen_GOTaddr2picreg ()); } void @@ -925,8 +990,8 @@ expand_epilogue (void) HOST_WIDE_INT size; /* SIZE includes the fixed stack space needed for function calls. */ - size = get_frame_size () + current_function_outgoing_args_size; - size += (current_function_outgoing_args_size ? 4 : 0); + size = get_frame_size () + crtl->outgoing_args_size; + size += (crtl->outgoing_args_size ? 4 : 0); if (TARGET_AM33_2 && fp_regs_to_save ()) { @@ -1014,7 +1079,7 @@ expand_epilogue (void) /* Consider using a1 in post-increment mode, as long as the user hasn't changed the calling conventions of a1. */ - if (call_used_regs[FIRST_ADDRESS_REGNUM+1] + if (call_really_used_regs [FIRST_ADDRESS_REGNUM + 1] && ! fixed_regs[FIRST_ADDRESS_REGNUM+1]) { /* Insn: mov sp,a1. */ @@ -1064,7 +1129,7 @@ expand_epilogue (void) + REG_SAVE_BYTES - 252))); size = 252 - REG_SAVE_BYTES - 4 * num_regs_to_save; break; - + case restore_a1: reg = gen_rtx_REG (SImode, FIRST_ADDRESS_REGNUM + 1); emit_insn (gen_movsi (reg, stack_pointer_rtx)); @@ -1073,7 +1138,7 @@ expand_epilogue (void) break; default: - abort (); + gcc_unreachable (); } } @@ -1082,10 +1147,10 @@ expand_epilogue (void) reg = gen_rtx_POST_INC (SImode, reg); for (i = FIRST_FP_REGNUM; i <= LAST_FP_REGNUM; ++i) - if (regs_ever_live[i] && ! call_used_regs[i]) + if (df_regs_ever_live_p (i) && ! call_really_used_regs [i]) { rtx addr; - + if (reg) addr = reg; else if (size) @@ -1122,7 +1187,7 @@ expand_epilogue (void) If the stack size + register save area is more than 255 bytes, then the stack must be cut back here since the size + register - save size is too big for a ret/retf instruction. + save size is too big for a ret/retf instruction. Else leave it alone, it will be cut back as part of the ret/retf instruction, or there wasn't any stack to begin with. @@ -1144,10 +1209,10 @@ expand_epilogue (void) } /* Adjust the stack and restore callee-saved registers, if any. */ - if (size || regs_ever_live[2] || regs_ever_live[3] - || regs_ever_live[6] || regs_ever_live[7] - || regs_ever_live[14] || regs_ever_live[15] - || regs_ever_live[16] || regs_ever_live[17] + if (size || df_regs_ever_live_p (2) || df_regs_ever_live_p (3) + || df_regs_ever_live_p (6) || df_regs_ever_live_p (7) + || df_regs_ever_live_p (14) || df_regs_ever_live_p (15) + || df_regs_ever_live_p (16) || df_regs_ever_live_p (17) || frame_pointer_needed) emit_jump_insn (gen_return_internal_regs (GEN_INT (size + REG_SAVE_BYTES))); @@ -1198,20 +1263,13 @@ notice_update_cc (rtx body, rtx insn) cc_status.mdep.fpCC = 1; break; - case CC_INVERT: - /* The insn is a compare instruction. */ - CC_STATUS_INIT; - cc_status.value1 = SET_SRC (body); - cc_status.flags |= CC_INVERTED; - break; - case CC_CLOBBER: /* Insn doesn't leave CC in a usable state. */ CC_STATUS_INIT; break; default: - abort (); + gcc_unreachable (); } } @@ -1294,36 +1352,31 @@ store_multiple_operation (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) return mask; } -/* Return true if OP is a valid call operand. */ - -int -call_address_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) -{ - if (flag_pic) - return (EXTRA_CONSTRAINT (op, 'S') || GET_CODE (op) == REG); - - return (GET_CODE (op) == SYMBOL_REF || GET_CODE (op) == REG); -} - /* What (if any) secondary registers are needed to move IN with mode - MODE into a register in register class CLASS. + MODE into a register in register class RCLASS. We might be able to simplify this. */ enum reg_class -secondary_reload_class (enum reg_class class, enum machine_mode mode, rtx in) +mn10300_secondary_reload_class (enum reg_class rclass, enum machine_mode mode, + rtx in) { + rtx inner = in; + + /* Strip off any SUBREG expressions from IN. Basically we want + to know if IN is a pseudo or (subreg (pseudo)) as those can + turn into MEMs during reload. */ + while (GET_CODE (inner) == SUBREG) + inner = SUBREG_REG (inner); + /* Memory loads less than a full word wide can't have an address or stack pointer destination. They must use a data register as an intermediate register. */ if ((GET_CODE (in) == MEM - || (GET_CODE (in) == REG - && REGNO (in) >= FIRST_PSEUDO_REGISTER) - || (GET_CODE (in) == SUBREG - && GET_CODE (SUBREG_REG (in)) == REG - && REGNO (SUBREG_REG (in)) >= FIRST_PSEUDO_REGISTER)) + || (GET_CODE (inner) == REG + && REGNO (inner) >= FIRST_PSEUDO_REGISTER)) && (mode == QImode || mode == HImode) - && (class == ADDRESS_REGS || class == SP_REGS - || class == SP_OR_ADDRESS_REGS)) + && (rclass == ADDRESS_REGS || rclass == SP_REGS + || rclass == SP_OR_ADDRESS_REGS)) { if (TARGET_AM33) return DATA_OR_EXTENDED_REGS; @@ -1332,12 +1385,12 @@ secondary_reload_class (enum reg_class class, enum machine_mode mode, rtx in) /* We can't directly load sp + const_int into a data register; we must use an address register as an intermediate. */ - if (class != SP_REGS - && class != ADDRESS_REGS - && class != SP_OR_ADDRESS_REGS - && class != SP_OR_EXTENDED_REGS - && class != ADDRESS_OR_EXTENDED_REGS - && class != SP_OR_ADDRESS_OR_EXTENDED_REGS + if (rclass != SP_REGS + && rclass != ADDRESS_REGS + && rclass != SP_OR_ADDRESS_REGS + && rclass != SP_OR_EXTENDED_REGS + && rclass != ADDRESS_OR_EXTENDED_REGS + && rclass != SP_OR_ADDRESS_OR_EXTENDED_REGS && (in == stack_pointer_rtx || (GET_CODE (in) == PLUS && (XEXP (in, 0) == stack_pointer_rtx @@ -1347,18 +1400,24 @@ secondary_reload_class (enum reg_class class, enum machine_mode mode, rtx in) if (GET_CODE (in) == PLUS && (XEXP (in, 0) == stack_pointer_rtx || XEXP (in, 1) == stack_pointer_rtx)) + return GENERAL_REGS; + + if (TARGET_AM33_2 + && rclass == FP_REGS) { - if (TARGET_AM33) - return DATA_OR_EXTENDED_REGS; - return DATA_REGS; - } - - if (TARGET_AM33_2 && class == FP_REGS - && GET_CODE (in) == MEM && ! OK_FOR_Q (in)) - { - if (TARGET_AM33) - return DATA_OR_EXTENDED_REGS; - return DATA_REGS; + /* We can't load directly into an FP register from a + constant address. */ + if (GET_CODE (in) == MEM + && CONSTANT_ADDRESS_P (XEXP (in, 0))) + return (TARGET_AM33 ? DATA_OR_EXTENDED_REGS : DATA_REGS); + + /* Handle case were a pseudo may not get a hard register + but has an equivalent memory location defined. */ + if (GET_CODE (inner) == REG + && REGNO (inner) >= FIRST_PSEUDO_REGISTER + && reg_equiv_mem [REGNO (inner)] + && CONSTANT_ADDRESS_P (XEXP (reg_equiv_mem [REGNO (inner)], 0))) + return (TARGET_AM33 ? DATA_OR_EXTENDED_REGS : DATA_REGS); } /* Otherwise assume no secondary reloads are needed. */ @@ -1372,10 +1431,10 @@ initial_offset (int from, int to) is the size of the callee register save area. */ if (from == ARG_POINTER_REGNUM && to == FRAME_POINTER_REGNUM) { - if (regs_ever_live[2] || regs_ever_live[3] - || regs_ever_live[6] || regs_ever_live[7] - || regs_ever_live[14] || regs_ever_live[15] - || regs_ever_live[16] || regs_ever_live[17] + if (df_regs_ever_live_p (2) || df_regs_ever_live_p (3) + || df_regs_ever_live_p (6) || df_regs_ever_live_p (7) + || df_regs_ever_live_p (14) || df_regs_ever_live_p (15) + || df_regs_ever_live_p (16) || df_regs_ever_live_p (17) || fp_regs_to_save () || frame_pointer_needed) return REG_SAVE_BYTES @@ -1389,20 +1448,20 @@ initial_offset (int from, int to) area, and the fixed stack space needed for function calls (if any). */ if (from == ARG_POINTER_REGNUM && to == STACK_POINTER_REGNUM) { - if (regs_ever_live[2] || regs_ever_live[3] - || regs_ever_live[6] || regs_ever_live[7] - || regs_ever_live[14] || regs_ever_live[15] - || regs_ever_live[16] || regs_ever_live[17] + if (df_regs_ever_live_p (2) || df_regs_ever_live_p (3) + || df_regs_ever_live_p (6) || df_regs_ever_live_p (7) + || df_regs_ever_live_p (14) || df_regs_ever_live_p (15) + || df_regs_ever_live_p (16) || df_regs_ever_live_p (17) || fp_regs_to_save () || frame_pointer_needed) return (get_frame_size () + REG_SAVE_BYTES + 4 * fp_regs_to_save () - + (current_function_outgoing_args_size - ? current_function_outgoing_args_size + 4 : 0)); + + (crtl->outgoing_args_size + ? crtl->outgoing_args_size + 4 : 0)); else return (get_frame_size () - + (current_function_outgoing_args_size - ? current_function_outgoing_args_size + 4 : 0)); + + (crtl->outgoing_args_size + ? crtl->outgoing_args_size + 4 : 0)); } /* The difference between the frame pointer and stack pointer is the sum @@ -1410,19 +1469,21 @@ initial_offset (int from, int to) for function calls (if any). */ if (from == FRAME_POINTER_REGNUM && to == STACK_POINTER_REGNUM) return (get_frame_size () - + (current_function_outgoing_args_size - ? current_function_outgoing_args_size + 4 : 0)); + + (crtl->outgoing_args_size + ? crtl->outgoing_args_size + 4 : 0)); - abort (); + gcc_unreachable (); } /* Worker function for TARGET_RETURN_IN_MEMORY. */ static bool -mn10300_return_in_memory (tree type, tree fntype ATTRIBUTE_UNUSED) +mn10300_return_in_memory (const_tree type, const_tree fntype ATTRIBUTE_UNUSED) { /* Return values > 8 bytes in length in memory. */ - return int_size_in_bytes (type) > 8 || TYPE_MODE (type) == BLKmode; + return (int_size_in_bytes (type) > 8 + || int_size_in_bytes (type) == 0 + || TYPE_MODE (type) == BLKmode); } /* Flush the argument registers to the stack for a stdarg function; @@ -1432,82 +1493,61 @@ mn10300_builtin_saveregs (void) { rtx offset, mem; tree fntype = TREE_TYPE (current_function_decl); - int argadj = ((!(TYPE_ARG_TYPES (fntype) != 0 - && (TREE_VALUE (tree_last (TYPE_ARG_TYPES (fntype))) - != void_type_node))) + int argadj = ((!stdarg_p (fntype)) ? UNITS_PER_WORD : 0); - int set = get_varargs_alias_set (); + alias_set_type set = get_varargs_alias_set (); if (argadj) - offset = plus_constant (current_function_arg_offset_rtx, argadj); + offset = plus_constant (crtl->args.arg_offset_rtx, argadj); else - offset = current_function_arg_offset_rtx; + offset = crtl->args.arg_offset_rtx; - mem = gen_rtx_MEM (SImode, current_function_internal_arg_pointer); + mem = gen_rtx_MEM (SImode, crtl->args.internal_arg_pointer); set_mem_alias_set (mem, set); emit_move_insn (mem, gen_rtx_REG (SImode, 0)); mem = gen_rtx_MEM (SImode, - plus_constant (current_function_internal_arg_pointer, 4)); + plus_constant (crtl->args.internal_arg_pointer, 4)); set_mem_alias_set (mem, set); emit_move_insn (mem, gen_rtx_REG (SImode, 1)); return copy_to_reg (expand_binop (Pmode, add_optab, - current_function_internal_arg_pointer, + crtl->args.internal_arg_pointer, offset, 0, 0, OPTAB_LIB_WIDEN)); } -void +static void mn10300_va_start (tree valist, rtx nextarg) { nextarg = expand_builtin_saveregs (); std_expand_builtin_va_start (valist, nextarg); } -rtx -mn10300_va_arg (tree valist, tree type) -{ - HOST_WIDE_INT align, rsize; - tree t, ptr, pptr; - - /* Compute the rounded size of the type. */ - align = PARM_BOUNDARY / BITS_PER_UNIT; - rsize = (((int_size_in_bytes (type) + align - 1) / align) * align); - - t = build (POSTINCREMENT_EXPR, TREE_TYPE (valist), valist, - build_int_2 ((rsize > 8 ? 4 : rsize), 0)); - TREE_SIDE_EFFECTS (t) = 1; +/* Return true when a parameter should be passed by reference. */ - ptr = build_pointer_type (type); - - /* "Large" types are passed by reference. */ - if (rsize > 8) - { - pptr = build_pointer_type (ptr); - t = build1 (NOP_EXPR, pptr, t); - TREE_SIDE_EFFECTS (t) = 1; +static bool +mn10300_pass_by_reference (CUMULATIVE_ARGS *cum ATTRIBUTE_UNUSED, + enum machine_mode mode, const_tree type, + bool named ATTRIBUTE_UNUSED) +{ + unsigned HOST_WIDE_INT size; - t = build1 (INDIRECT_REF, ptr, t); - TREE_SIDE_EFFECTS (t) = 1; - } + if (type) + size = int_size_in_bytes (type); else - { - t = build1 (NOP_EXPR, ptr, t); - TREE_SIDE_EFFECTS (t) = 1; - } + size = GET_MODE_SIZE (mode); - /* Calculate! */ - return expand_expr (t, NULL_RTX, Pmode, EXPAND_NORMAL); + return (size > 8 || size == 0); } /* Return an RTX to represent where a value with mode MODE will be returned - from a function. If the result is 0, the argument is pushed. */ + from a function. If the result is NULL_RTX, the argument is pushed. */ rtx function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode, tree type, int named ATTRIBUTE_UNUSED) { - rtx result = 0; + rtx result = NULL_RTX; int size, align; /* We only support using 2 data registers as argument registers. */ @@ -1527,35 +1567,35 @@ function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode, /* Don't pass this arg via a register if all the argument registers are used up. */ if (cum->nbytes > nregs * UNITS_PER_WORD) - return 0; + return result; /* Don't pass this arg via a register if it would be split between registers and memory. */ if (type == NULL_TREE && cum->nbytes + size > nregs * UNITS_PER_WORD) - return 0; + return result; switch (cum->nbytes / UNITS_PER_WORD) { case 0: - result = gen_rtx_REG (mode, 0); + result = gen_rtx_REG (mode, FIRST_ARGUMENT_REGNUM); break; case 1: - result = gen_rtx_REG (mode, 1); + result = gen_rtx_REG (mode, FIRST_ARGUMENT_REGNUM + 1); break; default: - result = 0; + break; } return result; } -/* Return the number of registers to use for an argument passed partially - in registers and partially in memory. */ +/* Return the number of bytes of registers to use for an argument passed + partially in registers and partially in memory. */ -int -function_arg_partial_nregs (CUMULATIVE_ARGS *cum, enum machine_mode mode, - tree type, int named ATTRIBUTE_UNUSED) +static int +mn10300_arg_partial_bytes (CUMULATIVE_ARGS *cum, enum machine_mode mode, + tree type, bool named ATTRIBUTE_UNUSED) { int size, align; @@ -1587,7 +1627,57 @@ function_arg_partial_nregs (CUMULATIVE_ARGS *cum, enum machine_mode mode, && cum->nbytes + size > nregs * UNITS_PER_WORD) return 0; - return (nregs * UNITS_PER_WORD - cum->nbytes) / UNITS_PER_WORD; + return nregs * UNITS_PER_WORD - cum->nbytes; +} + +/* Return the location of the function's value. This will be either + $d0 for integer functions, $a0 for pointers, or a PARALLEL of both + $d0 and $a0 if the -mreturn-pointer-on-do flag is set. Note that + we only return the PARALLEL for outgoing values; we do not want + callers relying on this extra copy. */ + +static rtx +mn10300_function_value (const_tree valtype, + const_tree fn_decl_or_type ATTRIBUTE_UNUSED, + bool outgoing) +{ + rtx rv; + enum machine_mode mode = TYPE_MODE (valtype); + + if (! POINTER_TYPE_P (valtype)) + return gen_rtx_REG (mode, FIRST_DATA_REGNUM); + else if (! TARGET_PTR_A0D0 || ! outgoing + || cfun->returns_struct) + return gen_rtx_REG (mode, FIRST_ADDRESS_REGNUM); + + rv = gen_rtx_PARALLEL (mode, rtvec_alloc (2)); + XVECEXP (rv, 0, 0) + = gen_rtx_EXPR_LIST (VOIDmode, + gen_rtx_REG (mode, FIRST_ADDRESS_REGNUM), + GEN_INT (0)); + + XVECEXP (rv, 0, 1) + = gen_rtx_EXPR_LIST (VOIDmode, + gen_rtx_REG (mode, FIRST_DATA_REGNUM), + GEN_INT (0)); + return rv; +} + +/* Implements TARGET_LIBCALL_VALUE. */ + +static rtx +mn10300_libcall_value (enum machine_mode mode, + const_rtx fun ATTRIBUTE_UNUSED) +{ + return gen_rtx_REG (mode, FIRST_DATA_REGNUM); +} + +/* Implements FUNCTION_VALUE_REGNO_P. */ + +bool +mn10300_function_value_regno_p (const unsigned int regno) +{ + return (regno == FIRST_DATA_REGNUM || regno == FIRST_ADDRESS_REGNUM); } /* Output a tst insn. */ @@ -1630,7 +1720,7 @@ output_tst (rtx operand, rtx insn) } /* Are we setting a data register to zero (this does not win for - address registers)? + address registers)? If it's a call clobbered register, have we past a call? @@ -1646,8 +1736,8 @@ output_tst (rtx operand, rtx insn) == REGNO_REG_CLASS (REGNO (operand))) && REGNO_REG_CLASS (REGNO (SET_DEST (set))) != EXTENDED_REGS && REGNO (SET_DEST (set)) != REGNO (operand) - && (!past_call - || !call_used_regs[REGNO (SET_DEST (set))])) + && (!past_call + || ! call_really_used_regs [REGNO (SET_DEST (set))])) { rtx xoperands[2]; xoperands[0] = operand; @@ -1665,8 +1755,8 @@ output_tst (rtx operand, rtx insn) != REGNO_REG_CLASS (REGNO (operand))) && REGNO_REG_CLASS (REGNO (SET_DEST (set))) == EXTENDED_REGS && REGNO (SET_DEST (set)) != REGNO (operand) - && (!past_call - || !call_used_regs[REGNO (SET_DEST (set))])) + && (!past_call + || ! call_really_used_regs [REGNO (SET_DEST (set))])) { rtx xoperands[2]; xoperands[0] = operand; @@ -1693,24 +1783,6 @@ impossible_plus_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) return 0; } -/* Return 1 if X is a CONST_INT that is only 8 bits wide. This is used - for the btst insn which may examine memory or a register (the memory - variant only allows an unsigned 8 bit integer). */ -int -const_8bit_operand (register rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) -{ - return (GET_CODE (op) == CONST_INT - && INTVAL (op) >= 0 - && INTVAL (op) < 256); -} - -/* Return true if the operand is the 1.0f constant. */ -int -const_1f_operand (register rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) -{ - return (op == CONST1_RTX (SFmode)); -} - /* Similarly, but when using a zero_extract pattern for a btst where the source operand might end up in memory. */ int @@ -1760,17 +1832,14 @@ symbolic_operand (register rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) OLDX is the address as it was before break_out_memory_refs was called. In some cases it is useful to look at this to decide what needs to be done. - MODE and WIN are passed so that this macro can use - GO_IF_LEGITIMATE_ADDRESS. - Normally it is always safe for this macro to do nothing. It exists to recognize opportunities to optimize the output. But on a few ports with segmented architectures and indexed addressing (mn10300, hppa) it is used to rewrite certain problematical addresses. */ rtx -legitimize_address (rtx x, rtx oldx ATTRIBUTE_UNUSED, - enum machine_mode mode ATTRIBUTE_UNUSED) +mn10300_legitimize_address (rtx x, rtx oldx ATTRIBUTE_UNUSED, + enum machine_mode mode ATTRIBUTE_UNUSED) { if (flag_pic && ! legitimate_pic_operand_p (x)) x = legitimize_pic_address (oldx, NULL_RTX); @@ -1847,12 +1916,10 @@ legitimate_pic_operand_p (rtx x) && (XINT (x, 1) == UNSPEC_PIC || XINT (x, 1) == UNSPEC_GOT || XINT (x, 1) == UNSPEC_GOTOFF - || XINT (x, 1) == UNSPEC_PLT)) + || XINT (x, 1) == UNSPEC_PLT + || XINT (x, 1) == UNSPEC_GOTSYM_OFF)) return 1; - if (GET_CODE (x) == QUEUED) - return legitimate_pic_operand_p (QUEUED_VAR (x)); - fmt = GET_RTX_FORMAT (GET_CODE (x)); for (i = GET_RTX_LENGTH (GET_CODE (x)) - 1; i >= 0; i--) { @@ -1871,6 +1938,69 @@ legitimate_pic_operand_p (rtx x) return 1; } +/* Return TRUE if the address X, taken from a (MEM:MODE X) rtx, is + legitimate, and FALSE otherwise. + + On the mn10300, the value in the address register must be + in the same memory space/segment as the effective address. + + This is problematical for reload since it does not understand + that base+index != index+base in a memory reference. + + Note it is still possible to use reg+reg addressing modes, + it's just much more difficult. For a discussion of a possible + workaround and solution, see the comments in pa.c before the + function record_unscaled_index_insn_codes. */ + +bool +mn10300_legitimate_address_p (enum machine_mode mode, rtx x, bool strict) +{ + if (CONSTANT_ADDRESS_P (x) + && (! flag_pic || legitimate_pic_operand_p (x))) + return TRUE; + + if (RTX_OK_FOR_BASE_P (x, strict)) + return TRUE; + + if (TARGET_AM33 + && GET_CODE (x) == POST_INC + && RTX_OK_FOR_BASE_P (XEXP (x, 0), strict) + && (mode == SImode || mode == SFmode || mode == HImode)) + return TRUE; + + if (GET_CODE (x) == PLUS) + { + rtx base = 0, index = 0; + + if (REG_P (XEXP (x, 0)) + && REGNO_STRICT_OK_FOR_BASE_P (REGNO (XEXP (x, 0)), strict)) + { + base = XEXP (x, 0); + index = XEXP (x, 1); + } + + if (REG_P (XEXP (x, 1)) + && REGNO_STRICT_OK_FOR_BASE_P (REGNO (XEXP (x, 1)), strict)) + { + base = XEXP (x, 1); + index = XEXP (x, 0); + } + + if (base != 0 && index != 0) + { + if (GET_CODE (index) == CONST_INT) + return TRUE; + if (GET_CODE (index) == CONST + && GET_CODE (XEXP (index, 0)) != PLUS + && (! flag_pic + || legitimate_pic_operand_p (index))) + return TRUE; + } + } + + return FALSE; +} + static int mn10300_address_cost_1 (rtx x, int *unsig) { @@ -1895,7 +2025,7 @@ mn10300_address_cost_1 (rtx x, int *unsig) return 5; default: - abort (); + gcc_unreachable (); } case PLUS: @@ -1909,7 +2039,7 @@ mn10300_address_cost_1 (rtx x, int *unsig) case EXPR_LIST: case SUBREG: case MEM: - return mn10300_address_cost (XEXP (x, 0)); + return mn10300_address_cost (XEXP (x, 0), !optimize_size); case ZERO_EXTEND: *unsig = 1; @@ -1931,40 +2061,27 @@ mn10300_address_cost_1 (rtx x, int *unsig) case LABEL_REF: return 8; - case ADDRESSOF: - switch (GET_CODE (XEXP (x, 0))) - { - case MEM: - return mn10300_address_cost (XEXP (x, 0)); - - case REG: - return 1; - - default: - abort (); - } - default: - abort (); + gcc_unreachable (); } } static int -mn10300_address_cost (rtx x) +mn10300_address_cost (rtx x, bool speed ATTRIBUTE_UNUSED) { int s = 0; return mn10300_address_cost_1 (x, &s); } static bool -mn10300_rtx_costs (rtx x, int code, int outer_code, int *total) +mn10300_rtx_costs (rtx x, int code, int outer_code, int *total, bool speed ATTRIBUTE_UNUSED) { switch (code) { case CONST_INT: /* Zeros are extremely cheap. */ - if (INTVAL (x) == 0 && outer_code == SET) + if (INTVAL (x) == 0 && (outer_code == SET || outer_code == COMPARE)) *total = 0; /* If it fits in 8 bits, then it's still relatively cheap. */ else if (INT_8_BITS (INTVAL (x))) @@ -1993,6 +2110,12 @@ mn10300_rtx_costs (rtx x, int code, int outer_code, int *total) *total = 8; return true; + case ZERO_EXTRACT: + /* This is cheap, we can use btst. */ + if (outer_code == COMPARE) + *total = 0; + return false; + /* ??? This probably needs more work. */ case MOD: case DIV: @@ -2028,7 +2151,7 @@ mn10300_wide_const_load_uses_clr (rtx operands[2]) val[1] = INTVAL (high); } break; - + case CONST_DOUBLE: if (GET_MODE (operands[1]) == DFmode) { @@ -2044,7 +2167,7 @@ mn10300_wide_const_load_uses_clr (rtx operands[2]) val[1] = CONST_DOUBLE_HIGH (operands[1]); } break; - + default: return false; } @@ -2068,3 +2191,106 @@ mn10300_encode_section_info (tree decl, rtx rtl, int first ATTRIBUTE_UNUSED) if (flag_pic) SYMBOL_REF_FLAG (symbol) = (*targetm.binds_local_p) (decl); } + +/* Dispatch tables on the mn10300 are extremely expensive in terms of code + and readonly data size. So we crank up the case threshold value to + encourage a series of if/else comparisons to implement many small switch + statements. In theory, this value could be increased much more if we + were solely optimizing for space, but we keep it "reasonable" to avoid + serious code efficiency lossage. */ + +unsigned int mn10300_case_values_threshold (void) +{ + return 6; +} + +/* Worker function for TARGET_ASM_TRAMPOLINE_TEMPLATE. */ + +static void +mn10300_asm_trampoline_template (FILE *f) +{ + fprintf (f, "\tadd -4,sp\n"); + fprintf (f, "\t.long 0x0004fffa\n"); + fprintf (f, "\tmov (0,sp),a0\n"); + fprintf (f, "\tadd 4,sp\n"); + fprintf (f, "\tmov (13,a0),a1\n"); + fprintf (f, "\tmov (17,a0),a0\n"); + fprintf (f, "\tjmp (a0)\n"); + fprintf (f, "\t.long 0\n"); + fprintf (f, "\t.long 0\n"); +} + +/* Worker function for TARGET_TRAMPOLINE_INIT. */ + +static void +mn10300_trampoline_init (rtx m_tramp, tree fndecl, rtx chain_value) +{ + rtx fnaddr = XEXP (DECL_RTL (fndecl), 0); + rtx mem; + + emit_block_move (m_tramp, assemble_trampoline_template (), + GEN_INT (TRAMPOLINE_SIZE), BLOCK_OP_NORMAL); + + mem = adjust_address (m_tramp, SImode, 0x14); + emit_move_insn (mem, chain_value); + mem = adjust_address (m_tramp, SImode, 0x18); + emit_move_insn (mem, fnaddr); +} + +/* Output the assembler code for a C++ thunk function. + THUNK_DECL is the declaration for the thunk function itself, FUNCTION + is the decl for the target function. DELTA is an immediate constant + offset to be added to the THIS parameter. If VCALL_OFFSET is nonzero + the word at the adjusted address *(*THIS' + VCALL_OFFSET) should be + additionally added to THIS. Finally jump to the entry point of + FUNCTION. */ + +static void +mn10300_asm_output_mi_thunk (FILE * file, + tree thunk_fndecl ATTRIBUTE_UNUSED, + HOST_WIDE_INT delta, + HOST_WIDE_INT vcall_offset, + tree function) +{ + const char * _this; + + /* Get the register holding the THIS parameter. Handle the case + where there is a hidden first argument for a returned structure. */ + if (aggregate_value_p (TREE_TYPE (TREE_TYPE (function)), function)) + _this = reg_names [FIRST_ARGUMENT_REGNUM + 1]; + else + _this = reg_names [FIRST_ARGUMENT_REGNUM]; + + fprintf (file, "\t%s Thunk Entry Point:\n", ASM_COMMENT_START); + + if (delta) + fprintf (file, "\tadd %d, %s\n", (int) delta, _this); + + if (vcall_offset) + { + const char * scratch = reg_names [FIRST_ADDRESS_REGNUM + 1]; + + fprintf (file, "\tmov %s, %s\n", _this, scratch); + fprintf (file, "\tmov (%s), %s\n", scratch, scratch); + fprintf (file, "\tadd %d, %s\n", (int) vcall_offset, scratch); + fprintf (file, "\tmov (%s), %s\n", scratch, scratch); + fprintf (file, "\tadd %s, %s\n", scratch, _this); + } + + fputs ("\tjmp ", file); + assemble_name (file, XSTR (XEXP (DECL_RTL (function), 0), 0)); + putc ('\n', file); +} + +/* Return true if mn10300_output_mi_thunk would be able to output the + assembler code for the thunk function specified by the arguments + it is passed, and false otherwise. */ + +static bool +mn10300_can_output_mi_thunk (const_tree thunk_fndecl ATTRIBUTE_UNUSED, + HOST_WIDE_INT delta ATTRIBUTE_UNUSED, + HOST_WIDE_INT vcall_offset ATTRIBUTE_UNUSED, + const_tree function ATTRIBUTE_UNUSED) +{ + return true; +}