From patchwork Tue Jun 14 04:08:37 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Wei Qin X-Patchwork-Id: 100263 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) by ozlabs.org (Postfix) with SMTP id E0371B6F8F for ; Tue, 14 Jun 2011 14:09:15 +1000 (EST) Received: (qmail 2415 invoked by alias); 14 Jun 2011 04:09:10 -0000 Received: (qmail 1871 invoked by uid 22791); 14 Jun 2011 04:09:00 -0000 X-SWARE-Spam-Status: No, hits=-0.2 required=5.0 tests=AWL, BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, FREEMAIL_FROM, KAM_STOCKGEN, RCVD_IN_DNSWL_LOW, RFC_ABUSE_POST, TW_CP, TW_FN, T_TO_NO_BRKTS_FREEMAIL X-Spam-Check-By: sourceware.org Received: from mail-pv0-f175.google.com (HELO mail-pv0-f175.google.com) (74.125.83.175) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Tue, 14 Jun 2011 04:08:38 +0000 Received: by pvc30 with SMTP id 30so2578511pvc.20 for ; Mon, 13 Jun 2011 21:08:38 -0700 (PDT) MIME-Version: 1.0 Received: by 10.143.60.1 with SMTP id n1mr901808wfk.331.1308024517422; Mon, 13 Jun 2011 21:08:37 -0700 (PDT) Received: by 10.142.169.17 with HTTP; Mon, 13 Jun 2011 21:08:37 -0700 (PDT) Date: Tue, 14 Jun 2011 12:08:37 +0800 Message-ID: Subject: Merge score7 to score.c and remove forwarding functions(with score_handle_option merged). From: Wei Qin To: gcc-patches@gcc.gnu.org X-IsSubscribed: yes Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Delivered-To: mailing list gcc-patches@gcc.gnu.org Merge score7 to score.c and remove forwarding functions. Merge score_handle_option. Delete score7.c and score7.h. 2011-06-13 Wei Qin * config.gcc (score-*-elf): Remove score7.o. * config/score/t-score-elf: Likewise. * config/score/score.c: Merge score7 to score.c and remove forwarding functions. * config/score/score7.c Deleted. * config/score/score7.h Deleted. Index: gcc/ChangeLog =================================================================== --- gcc/ChangeLog (revision 175007) +++ gcc/ChangeLog (working copy) @@ -1,3 +1,11 @@ +2011-06-13 Wei Qin + + * config.gcc (score-*-elf): Remove score7.o. + * config/score/t-score-elf: Likewise. + * config/score/score.c: Merge score7 to score.c and remove forwarding functions. + * config/score/score7.c Deleted. + * config/score/score7.h Deleted. + 2011-06-13 David Edelsohn * config/rs6000/rs6000.md (movdi_mfpgpr): Remove POWER mnemonic. Index: gcc/config.gcc =================================================================== --- gcc/config.gcc (revision 175007) +++ gcc/config.gcc (working copy) @@ -2233,7 +2233,6 @@ tm_file="dbxelf.h elfos.h score/elf.h score/score.h newlib-stdint.h" extra_parts="crti.o crtn.o crtbegin.o crtend.o" tmake_file="${tmake_file} score/t-score-elf score/t-score-softfp soft-fp/t-softfp" - extra_objs="score7.o" ;; sh-*-elf* | sh[12346l]*-*-elf* | \ sh-*-linux* | sh[2346lbe]*-*-linux* | \ Index: gcc/config/score/t-score-elf =================================================================== --- gcc/config/score/t-score-elf (revision 175007) +++ gcc/config/score/t-score-elf (working copy) @@ -16,13 +16,6 @@ # along with GCC; see the file COPYING3. If not see # . -# Additional Backend Files -score7.o: $(srcdir)/config/score/score7.c $(CONFIG_H) $(SYSTEM_H) \ - coretypes.h $(TM_H) $(RTL_H) output.h flags.h $(TREE_H) \ - expr.h toplev.h $(TM_P_H) - $(COMPILER) -c $(ALL_COMPILERFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) \ - $(srcdir)/config/score/score7.c - # Assemble startup files. $(T)crti.o: $(srcdir)/config/score/crti.asm $(GCC_PASSES) $(GCC_FOR_TARGET) $(GCC_CFLAGS) $(MULTILIB_CFLAGS) $(INCLUDES) \ Index: gcc/config/score/score.c =================================================================== --- gcc/config/score/score.c (revision 175007) +++ gcc/config/score/score.c (working copy) @@ -47,19 +47,72 @@ #include "target-def.h" #include "integrate.h" #include "langhooks.h" -#include "score7.h" #include "df.h" #include "opts.h" -static void score_option_override (void); +#define SCORE_SDATA_MAX score_sdata_max +#define SCORE_STACK_ALIGN(LOC) (((LOC) + 3) & ~3) +#define SCORE_PROLOGUE_TEMP_REGNUM (GP_REG_FIRST + 8) +#define SCORE_EPILOGUE_TEMP_REGNUM (GP_REG_FIRST + 8) +#define SCORE_DEFAULT_SDATA_MAX 8 +#define BITSET_P(VALUE, BIT) (((VALUE) & (1L << (BIT))) != 0) +#define INS_BUF_SZ 128 + +enum score_address_type +{ + SCORE_ADD_REG, + SCORE_ADD_CONST_INT, + SCORE_ADD_SYMBOLIC +}; + +struct score_frame_info +{ + HOST_WIDE_INT total_size; /* bytes that the entire frame takes up */ + HOST_WIDE_INT var_size; /* bytes that variables take up */ + HOST_WIDE_INT args_size; /* bytes that outgoing arguments take up */ + HOST_WIDE_INT gp_reg_size; /* bytes needed to store gp regs */ + HOST_WIDE_INT gp_sp_offset; /* offset from new sp to store gp registers */ + HOST_WIDE_INT cprestore_size; /* # bytes that the .cprestore slot takes up */ + unsigned int mask; /* mask of saved gp registers */ + int num_gp; /* number of gp registers saved */ +}; + +struct score_arg_info +{ + unsigned int num_bytes; /* The argument's size in bytes */ + unsigned int reg_words; /* The number of words passed in registers */ + unsigned int reg_offset; /* The offset of the first register from */ + /* GP_ARG_FIRST or FP_ARG_FIRST etc */ + unsigned int stack_words; /* The number of words that must be passed */ + /* on the stack */ + unsigned int stack_offset; /* The offset from the start of the stack */ + /* overflow area */ +}; + +#ifdef RTX_CODE +struct score_address_info +{ + enum score_address_type type; + rtx reg; + rtx offset; + enum rtx_code code; + enum score_symbol_type symbol_type; +}; +#endif + +static int score_sdata_max; +static char score_ins[INS_BUF_SZ + 8]; + /* Implement TARGET_OPTION_OPTIMIZATION_TABLE. */ static const struct default_options score_option_optimization_table[] = - { +{ { OPT_LEVELS_1_PLUS, OPT_fomit_frame_pointer, NULL, 1 }, { OPT_LEVELS_NONE, 0, NULL, 0 } - }; +}; +struct extern_list *extern_head = 0; + #undef TARGET_ASM_FILE_START #define TARGET_ASM_FILE_START score_asm_file_start @@ -149,31 +202,261 @@ #undef TARGET_TRAMPOLINE_INIT #define TARGET_TRAMPOLINE_INIT score_trampoline_init -struct extern_list *extern_head = 0; +/* Return true if SYMBOL is a SYMBOL_REF and OFFSET + SYMBOL points + to the same object as SYMBOL. */ +static int +score_offset_within_object_p (rtx symbol, HOST_WIDE_INT offset) +{ + if (GET_CODE (symbol) != SYMBOL_REF) + return 0; -/* default 0 = NO_REGS */ -enum reg_class score_char_to_class[256]; + if (CONSTANT_POOL_ADDRESS_P (symbol) + && offset >= 0 + && offset < (int)GET_MODE_SIZE (get_pool_mode (symbol))) + return 1; + if (SYMBOL_REF_DECL (symbol) != 0 + && offset >= 0 + && offset < int_size_in_bytes (TREE_TYPE (SYMBOL_REF_DECL (symbol)))) + return 1; + + return 0; +} + +/* Split X into a base and a constant offset, storing them in *BASE + and *OFFSET respectively. */ +static void +score_split_const (rtx x, rtx *base, HOST_WIDE_INT *offset) +{ + *offset = 0; + + if (GET_CODE (x) == CONST) + x = XEXP (x, 0); + + if (GET_CODE (x) == PLUS && GET_CODE (XEXP (x, 1)) == CONST_INT) + { + *offset += INTVAL (XEXP (x, 1)); + x = XEXP (x, 0); + } + + *base = x; +} + +/* Classify symbol X, which must be a SYMBOL_REF or a LABEL_REF. */ +static enum score_symbol_type +score_classify_symbol (rtx x) +{ + if (GET_CODE (x) == LABEL_REF) + return SYMBOL_GENERAL; + + gcc_assert (GET_CODE (x) == SYMBOL_REF); + + if (CONSTANT_POOL_ADDRESS_P (x)) + { + if (GET_MODE_SIZE (get_pool_mode (x)) <= SCORE_SDATA_MAX) + return SYMBOL_SMALL_DATA; + return SYMBOL_GENERAL; + } + if (SYMBOL_REF_SMALL_P (x)) + return SYMBOL_SMALL_DATA; + return SYMBOL_GENERAL; +} + +/* Return true if the current function must save REGNO. */ +static int +score_save_reg_p (unsigned int regno) +{ + /* Check call-saved registers. */ + if (df_regs_ever_live_p (regno) && !call_used_regs[regno]) + return 1; + + /* We need to save the old frame pointer before setting up a new one. */ + if (regno == HARD_FRAME_POINTER_REGNUM && frame_pointer_needed) + return 1; + + /* We need to save the incoming return address if it is ever clobbered + within the function. */ + if (regno == RA_REGNUM && df_regs_ever_live_p (regno)) + return 1; + + return 0; +} + +/* Return one word of double-word value OP, taking into account the fixed + endianness of certain registers. HIGH_P is true to select the high part, + false to select the low part. */ +static rtx +score_subw (rtx op, int high_p) +{ + unsigned int byte; + enum machine_mode mode = GET_MODE (op); + + if (mode == VOIDmode) + mode = DImode; + + byte = (TARGET_LITTLE_ENDIAN ? high_p : !high_p) ? UNITS_PER_WORD : 0; + + if (GET_CODE (op) == REG && REGNO (op) == HI_REGNUM) + return gen_rtx_REG (SImode, high_p ? HI_REGNUM : LO_REGNUM); + + if (GET_CODE (op) == MEM) + return adjust_address (op, SImode, byte); + + return simplify_gen_subreg (SImode, op, mode, byte); +} + +static struct score_frame_info * +score_cached_frame (void) +{ + static struct score_frame_info _frame_info; + return &_frame_info; +} + +/* Return the bytes needed to compute the frame pointer from the current + stack pointer. SIZE is the size (in bytes) of the local variables. */ +static struct score_frame_info * +score_compute_frame_size (HOST_WIDE_INT size) +{ + unsigned int regno; + struct score_frame_info *f = score_cached_frame (); + + memset (f, 0, sizeof (struct score_frame_info)); + f->gp_reg_size = 0; + f->mask = 0; + f->var_size = SCORE_STACK_ALIGN (size); + f->args_size = crtl->outgoing_args_size; + f->cprestore_size = flag_pic ? UNITS_PER_WORD : 0; + if (f->var_size == 0 && current_function_is_leaf) + f->args_size = f->cprestore_size = 0; + + if (f->args_size == 0 && cfun->calls_alloca) + f->args_size = UNITS_PER_WORD; + + f->total_size = f->var_size + f->args_size + f->cprestore_size; + for (regno = GP_REG_FIRST; regno <= GP_REG_LAST; regno++) + { + if (score_save_reg_p (regno)) + { + f->gp_reg_size += GET_MODE_SIZE (SImode); + f->mask |= 1 << (regno - GP_REG_FIRST); + } + } + + if (crtl->calls_eh_return) + { + unsigned int i; + for (i = 0;; ++i) + { + regno = EH_RETURN_DATA_REGNO (i); + if (regno == INVALID_REGNUM) + break; + f->gp_reg_size += GET_MODE_SIZE (SImode); + f->mask |= 1 << (regno - GP_REG_FIRST); + } + } + + f->total_size += f->gp_reg_size; + f->num_gp = f->gp_reg_size / UNITS_PER_WORD; + + if (f->mask) + { + HOST_WIDE_INT offset; + offset = (f->args_size + f->cprestore_size + f->var_size + + f->gp_reg_size - GET_MODE_SIZE (SImode)); + f->gp_sp_offset = offset; + } + else + f->gp_sp_offset = 0; + + return f; +} + +/* Return true if X is a valid base register for the given mode. + Allow only hard registers if STRICT. */ +static int +score_valid_base_register_p (rtx x, int strict) +{ + if (!strict && GET_CODE (x) == SUBREG) + x = SUBREG_REG (x); + + return (GET_CODE (x) == REG + && score_regno_mode_ok_for_base_p (REGNO (x), strict)); +} + +/* Return true if X is a valid address for machine mode MODE. If it is, + fill in INFO appropriately. STRICT is true if we should only accept + hard base registers. */ +static int +score_classify_address (struct score_address_info *info, + enum machine_mode mode, rtx x, int strict) +{ + info->code = GET_CODE (x); + + switch (info->code) + { + case REG: + case SUBREG: + info->type = SCORE_ADD_REG; + info->reg = x; + info->offset = const0_rtx; + return score_valid_base_register_p (info->reg, strict); + case PLUS: + info->type = SCORE_ADD_REG; + info->reg = XEXP (x, 0); + info->offset = XEXP (x, 1); + return (score_valid_base_register_p (info->reg, strict) + && GET_CODE (info->offset) == CONST_INT + && IMM_IN_RANGE (INTVAL (info->offset), 15, 1)); + case PRE_DEC: + case POST_DEC: + case PRE_INC: + case POST_INC: + if (GET_MODE_SIZE (mode) > GET_MODE_SIZE (SImode)) + return false; + info->type = SCORE_ADD_REG; + info->reg = XEXP (x, 0); + info->offset = GEN_INT (GET_MODE_SIZE (mode)); + return score_valid_base_register_p (info->reg, strict); + case CONST_INT: + info->type = SCORE_ADD_CONST_INT; + return IMM_IN_RANGE (INTVAL (x), 15, 1); + case CONST: + case LABEL_REF: + case SYMBOL_REF: + info->type = SCORE_ADD_SYMBOLIC; + return (score_symbolic_constant_p (x, &info->symbol_type) + && (info->symbol_type == SYMBOL_GENERAL + || info->symbol_type == SYMBOL_SMALL_DATA)); + default: + return 0; + } +} + /* Implement TARGET_RETURN_IN_MEMORY. In S+core, small structures are returned in a register. Objects with varying size must still be returned in memory. */ static bool score_return_in_memory (const_tree type, const_tree fndecl ATTRIBUTE_UNUSED) { - if (TARGET_SCORE7 || TARGET_SCORE7D) - return score7_return_in_memory (type, fndecl); - else - gcc_unreachable (); + return ((TYPE_MODE (type) == BLKmode) + || (int_size_in_bytes (type) > 2 * UNITS_PER_WORD) + || (int_size_in_bytes (type) == -1)); } -/* Return nonzero when an argument must be passed by reference. */ -static bool -score_pass_by_reference (CUMULATIVE_ARGS *cum ATTRIBUTE_UNUSED, - enum machine_mode mode, const_tree type, - bool named ATTRIBUTE_UNUSED) +/* Return a legitimate address for REG + OFFSET. */ +static rtx +score_add_offset (rtx reg, HOST_WIDE_INT offset) { - /* If we have a variable-sized parameter, we have no choice. */ - return targetm.calls.must_pass_in_stack (mode, type); + if (!IMM_IN_RANGE (offset, 15, 1)) + { + reg = expand_simple_binop (GET_MODE (reg), PLUS, + gen_int_mode (offset & 0xffffc000, + GET_MODE (reg)), + reg, NULL, 0, OPTAB_WIDEN); + offset &= 0x3fff; + } + + return plus_constant (reg, offset); } /* Implement TARGET_ASM_OUTPUT_MI_THUNK. Generate rtl rather than asm text @@ -183,89 +466,279 @@ HOST_WIDE_INT delta, HOST_WIDE_INT vcall_offset, tree function) { - if (TARGET_SCORE7 || TARGET_SCORE7D) - score7_output_mi_thunk (file, thunk_fndecl, delta, vcall_offset, function); + rtx this_rtx, temp1, insn, fnaddr; + + /* Pretend to be a post-reload pass while generating rtl. */ + reload_completed = 1; + + /* Mark the end of the (empty) prologue. */ + emit_note (NOTE_INSN_PROLOGUE_END); + + /* We need two temporary registers in some cases. */ + temp1 = gen_rtx_REG (Pmode, 8); + + /* Find out which register contains the "this" pointer. */ + if (aggregate_value_p (TREE_TYPE (TREE_TYPE (function)), function)) + this_rtx = gen_rtx_REG (Pmode, ARG_REG_FIRST + 1); else - gcc_unreachable (); + this_rtx = gen_rtx_REG (Pmode, ARG_REG_FIRST); + + /* Add DELTA to THIS_RTX. */ + if (delta != 0) + { + rtx offset = GEN_INT (delta); + if (!(delta >= -32768 && delta <= 32767)) + { + emit_move_insn (temp1, offset); + offset = temp1; + } + emit_insn (gen_add3_insn (this_rtx, this_rtx, offset)); + } + + /* If needed, add *(*THIS_RTX + VCALL_OFFSET) to THIS_RTX. */ + if (vcall_offset != 0) + { + rtx addr; + + /* Set TEMP1 to *THIS_RTX. */ + emit_move_insn (temp1, gen_rtx_MEM (Pmode, this_rtx)); + + /* Set ADDR to a legitimate address for *THIS_RTX + VCALL_OFFSET. */ + addr = score_add_offset (temp1, vcall_offset); + + /* Load the offset and add it to THIS_RTX. */ + emit_move_insn (temp1, gen_rtx_MEM (Pmode, addr)); + emit_insn (gen_add3_insn (this_rtx, this_rtx, temp1)); + } + + /* Jump to the target function. */ + fnaddr = XEXP (DECL_RTL (function), 0); + insn = emit_call_insn (gen_sibcall_internal_score7 (fnaddr, const0_rtx)); + SIBLING_CALL_P (insn) = 1; + + /* Run just enough of rest_of_compilation. This sequence was + "borrowed" from alpha.c. */ + insn = get_insns (); + insn_locators_alloc (); + split_all_insns_noflow (); + shorten_branches (insn); + final_start_function (insn, file, 1); + final (insn, file, 1); + final_end_function (); + + /* Clean up the vars set above. Note that final_end_function resets + the global pointer for us. */ + reload_completed = 0; } -/* Implement TARGET_FUNCTION_OK_FOR_SIBCALL. */ -static bool -score_function_ok_for_sibcall (ATTRIBUTE_UNUSED tree decl, - ATTRIBUTE_UNUSED tree exp) +/* Copy VALUE to a register and return that register. If new psuedos + are allowed, copy it into a new register, otherwise use DEST. */ +static rtx +score_force_temporary (rtx dest, rtx value) { - return true; + if (can_create_pseudo_p ()) + return force_reg (Pmode, value); + else + { + emit_move_insn (copy_rtx (dest), value); + return dest; + } } +/* Return a LO_SUM expression for ADDR. TEMP is as for score_force_temporary + and is used to load the high part into a register. */ +static rtx +score_split_symbol (rtx temp, rtx addr) +{ + rtx high = score_force_temporary (temp, + gen_rtx_HIGH (Pmode, copy_rtx (addr))); + return gen_rtx_LO_SUM (Pmode, high, addr); +} + +/* This function is used to implement LEGITIMIZE_ADDRESS. If X can + be legitimized in a way that the generic machinery might not expect, + return the new address. */ +static rtx +score_legitimize_address (rtx x) +{ + enum score_symbol_type symbol_type; + + if (score_symbolic_constant_p (x, &symbol_type) + && symbol_type == SYMBOL_GENERAL) + return score_split_symbol (0, x); + + if (GET_CODE (x) == PLUS + && GET_CODE (XEXP (x, 1)) == CONST_INT) + { + rtx reg = XEXP (x, 0); + if (!score_valid_base_register_p (reg, 0)) + reg = copy_to_mode_reg (Pmode, reg); + return score_add_offset (reg, INTVAL (XEXP (x, 1))); + } + + return x; +} + +/* Fill INFO with information about a single argument. CUM is the + cumulative state for earlier arguments. MODE is the mode of this + argument and TYPE is its type (if known). NAMED is true if this + is a named (fixed) argument rather than a variable one. */ +static void +score_classify_arg (const CUMULATIVE_ARGS *cum, enum machine_mode mode, + const_tree type, bool named, struct score_arg_info *info) +{ + int even_reg_p; + unsigned int num_words, max_regs; + + even_reg_p = 0; + if (GET_MODE_CLASS (mode) == MODE_INT + || GET_MODE_CLASS (mode) == MODE_FLOAT) + even_reg_p = (GET_MODE_SIZE (mode) > UNITS_PER_WORD); + else + if (type != NULL_TREE && TYPE_ALIGN (type) > BITS_PER_WORD && named) + even_reg_p = 1; + + if (TARGET_MUST_PASS_IN_STACK (mode, type)) + info->reg_offset = ARG_REG_NUM; + else + { + info->reg_offset = cum->num_gprs; + if (even_reg_p) + info->reg_offset += info->reg_offset & 1; + } + + if (mode == BLKmode) + info->num_bytes = int_size_in_bytes (type); + else + info->num_bytes = GET_MODE_SIZE (mode); + + num_words = (info->num_bytes + UNITS_PER_WORD - 1) / UNITS_PER_WORD; + max_regs = ARG_REG_NUM - info->reg_offset; + + /* Partition the argument between registers and stack. */ + info->reg_words = MIN (num_words, max_regs); + info->stack_words = num_words - info->reg_words; + + /* The alignment applied to registers is also applied to stack arguments. */ + if (info->stack_words) + { + info->stack_offset = cum->stack_words; + if (even_reg_p) + info->stack_offset += info->stack_offset & 1; + } +} + /* Set up the stack and frame (if desired) for the function. */ static void score_function_prologue (FILE *file, HOST_WIDE_INT size ATTRIBUTE_UNUSED) { - if (TARGET_SCORE7 || TARGET_SCORE7D) - score7_function_prologue (file, size); - else - gcc_unreachable (); + const char *fnname; + struct score_frame_info *f = score_cached_frame (); + HOST_WIDE_INT tsize = f->total_size; + + fnname = XSTR (XEXP (DECL_RTL (current_function_decl), 0), 0); + if (!flag_inhibit_size_directive) + { + fputs ("\t.ent\t", file); + assemble_name (file, fnname); + fputs ("\n", file); + } + assemble_name (file, fnname); + fputs (":\n", file); + + if (!flag_inhibit_size_directive) + { + fprintf (file, + "\t.frame\t%s," HOST_WIDE_INT_PRINT_DEC ",%s, %d\t\t" + "# vars= " HOST_WIDE_INT_PRINT_DEC ", regs= %d" + ", args= " HOST_WIDE_INT_PRINT_DEC + ", gp= " HOST_WIDE_INT_PRINT_DEC "\n", + (reg_names[(frame_pointer_needed) + ? HARD_FRAME_POINTER_REGNUM : STACK_POINTER_REGNUM]), + tsize, + reg_names[RA_REGNUM], + current_function_is_leaf ? 1 : 0, + f->var_size, + f->num_gp, + f->args_size, + f->cprestore_size); + + fprintf(file, "\t.mask\t0x%08x," HOST_WIDE_INT_PRINT_DEC "\n", + f->mask, + (f->gp_sp_offset - f->total_size)); + } } /* Do any necessary cleanup after a function to restore stack, frame, and regs. */ static void -score_function_epilogue (FILE *file, - HOST_WIDE_INT size ATTRIBUTE_UNUSED) +score_function_epilogue (FILE *file, HOST_WIDE_INT size ATTRIBUTE_UNUSED) { - if (TARGET_SCORE7 || TARGET_SCORE7D) - score7_function_epilogue (file, size); - else - gcc_unreachable (); + if (!flag_inhibit_size_directive) + { + const char *fnname; + fnname = XSTR (XEXP (DECL_RTL (current_function_decl), 0), 0); + fputs ("\t.end\t", file); + assemble_name (file, fnname); + fputs ("\n", file); + } } -/* Implement TARGET_SCHED_ISSUE_RATE. */ -static int -score_issue_rate (void) +/* Returns true if X contains a SYMBOL_REF. */ +static bool +score_symbolic_expression_p (rtx x) { - return 1; + if (GET_CODE (x) == SYMBOL_REF) + return true; + + if (GET_CODE (x) == CONST) + return score_symbolic_expression_p (XEXP (x, 0)); + + if (UNARY_P (x)) + return score_symbolic_expression_p (XEXP (x, 0)); + + if (ARITHMETIC_P (x)) + return (score_symbolic_expression_p (XEXP (x, 0)) + || score_symbolic_expression_p (XEXP (x, 1))); + + return false; } /* Choose the section to use for the constant rtx expression X that has mode MODE. */ static section * -score_select_rtx_section (enum machine_mode mode, rtx x, - unsigned HOST_WIDE_INT align) +score_select_rtx_section (enum machine_mode mode, rtx x, unsigned HOST_WIDE_INT align) { - if (TARGET_SCORE7 || TARGET_SCORE7D) - return score7_select_rtx_section (mode, x, align); + if (GET_MODE_SIZE (mode) <= SCORE_SDATA_MAX) + return get_named_section (0, ".sdata", 0); + else if (flag_pic && score_symbolic_expression_p (x)) + return get_named_section (0, ".data.rel.ro", 3); else - gcc_unreachable (); + return mergeable_constant_section (mode, align, 0); } /* Implement TARGET_IN_SMALL_DATA_P. */ static bool score_in_small_data_p (const_tree decl) { - if (TARGET_SCORE7 || TARGET_SCORE7D) - return score7_in_small_data_p (decl); - else - gcc_unreachable (); -} + HOST_WIDE_INT size; -/* Implement TARGET_ASM_FILE_START. */ -static void -score_asm_file_start (void) -{ - if (TARGET_SCORE7D) - fprintf (asm_out_file, "# Sunplus S+core7d %s rev=%s\n", - TARGET_LITTLE_ENDIAN ? "el" : "eb", SCORE_GCC_VERSION); - else if (TARGET_SCORE7) - fprintf (asm_out_file, "# Sunplus S+core7 %s rev=%s\n", - TARGET_LITTLE_ENDIAN ? "el" : "eb", SCORE_GCC_VERSION); - else - fprintf (asm_out_file, "# Sunplus S+core unknown %s rev=%s\n", - TARGET_LITTLE_ENDIAN ? "el" : "eb", SCORE_GCC_VERSION); + if (TREE_CODE (decl) == STRING_CST + || TREE_CODE (decl) == FUNCTION_DECL) + return false; - default_file_start (); - - if (flag_pic) - fprintf (asm_out_file, "\t.set pic\n"); + if (TREE_CODE (decl) == VAR_DECL && DECL_SECTION_NAME (decl) != 0) + { + const char *name; + name = TREE_STRING_POINTER (DECL_SECTION_NAME (decl)); + if (strcmp (name, ".sdata") != 0 + && strcmp (name, ".sbss") != 0) + return true; + if (!DECL_EXTERNAL (decl)) + return false; + } + size = int_size_in_bytes (TREE_TYPE (decl)); + return (size > 0 && size <= SCORE_SDATA_MAX); } /* Implement TARGET_ASM_FILE_END. When using assembler macros, emit @@ -273,38 +746,23 @@ static void score_asm_file_end (void) { - if (TARGET_SCORE7 || TARGET_SCORE7D) - score7_asm_file_end (); - else - gcc_unreachable (); -} - -#define MASK_ALL_CPU_BITS (MASK_SCORE7 | MASK_SCORE7D) - -/* Implement TARGET_HANDLE_OPTION. */ -static bool -score_handle_option (struct gcc_options *opts, - struct gcc_options *opts_set ATTRIBUTE_UNUSED, - const struct cl_decoded_option *decoded, - location_t loc ATTRIBUTE_UNUSED) -{ - size_t code = decoded->opt_index; - int value = decoded->value; - - switch (code) + tree name_tree; + struct extern_list *p; + if (extern_head) { - case OPT_mscore7d: - opts->x_target_flags &= ~(MASK_ALL_CPU_BITS); - opts->x_target_flags |= MASK_SCORE7 | MASK_SCORE7D; - return true; - - case OPT_march_: - opts->x_target_flags &= ~(MASK_ALL_CPU_BITS); - opts->x_target_flags |= value; - return true; - - default: - return true; + fputs ("\n", asm_out_file); + for (p = extern_head; p != 0; p = p->next) + { + name_tree = get_identifier (p->name); + if (!TREE_ASM_WRITTEN (name_tree) + && TREE_SYMBOL_REFERENCED (name_tree)) + { + TREE_ASM_WRITTEN (name_tree) = 1; + fputs ("\t.extern\t", asm_out_file); + assemble_name (asm_out_file, p->name); + fprintf (asm_out_file, ", %d\n", p->size); + } + } } } @@ -312,28 +770,38 @@ static void score_option_override (void) { - if (TARGET_SCORE7 || TARGET_SCORE7D) - score7_option_override (); + flag_pic = false; + score_sdata_max = SCORE_DEFAULT_SDATA_MAX; + } /* Implement REGNO_REG_CLASS macro. */ int score_reg_class (int regno) { - if (TARGET_SCORE7 || TARGET_SCORE7D) - return score7_reg_class (regno); - else - gcc_unreachable (); + int c; + gcc_assert (regno >= 0 && regno < FIRST_PSEUDO_REGISTER); + + if (regno == FRAME_POINTER_REGNUM + || regno == ARG_POINTER_REGNUM) + return ALL_REGS; + + for (c = 0; c < N_REG_CLASSES; c++) + if (TEST_HARD_REG_BIT (reg_class_contents[c], regno)) + return c; + + return NO_REGS; } /* Implement PREFERRED_RELOAD_CLASS macro. */ enum reg_class score_preferred_reload_class (rtx x ATTRIBUTE_UNUSED, enum reg_class rclass) { - if (TARGET_SCORE7 || TARGET_SCORE7D) - return score7_preferred_reload_class (x, rclass); - else - gcc_unreachable (); + if (reg_class_subset_p (G16_REGS, rclass)) + return G16_REGS; + if (reg_class_subset_p (G32_REGS, rclass)) + return G32_REGS; + return rclass; } /* Implement SECONDARY_INPUT_RELOAD_CLASS @@ -343,10 +811,13 @@ enum machine_mode mode ATTRIBUTE_UNUSED, rtx x) { - if (TARGET_SCORE7 || TARGET_SCORE7D) - return score7_secondary_reload_class (rclass, mode, x); - else - gcc_unreachable (); + int regno = -1; + if (GET_CODE (x) == REG || GET_CODE(x) == SUBREG) + regno = true_regnum (x); + + if (!GR_REG_CLASS_P (rclass)) + return GP_REG_P (regno) ? NO_REGS : G32_REGS; + return NO_REGS; } @@ -355,22 +826,25 @@ int score_hard_regno_mode_ok (unsigned int regno, enum machine_mode mode) { - if (TARGET_SCORE7 || TARGET_SCORE7D) - return score7_hard_regno_mode_ok (regno, mode); + int size = GET_MODE_SIZE (mode); + enum mode_class mclass = GET_MODE_CLASS (mode); + + if (mclass == MODE_CC) + return regno == CC_REGNUM; + else if (regno == FRAME_POINTER_REGNUM + || regno == ARG_POINTER_REGNUM) + return mclass == MODE_INT; + else if (GP_REG_P (regno)) + /* ((regno <= (GP_REG_LAST- HARD_REGNO_NREGS (dummy, mode)) + 1) */ + return !(regno & 1) || (size <= UNITS_PER_WORD); + else if (CE_REG_P (regno)) + return (mclass == MODE_INT + && ((size <= UNITS_PER_WORD) + || (regno == CE_REG_FIRST && size == 2 * UNITS_PER_WORD))); else - gcc_unreachable (); + return (mclass == MODE_INT) && (size <= UNITS_PER_WORD); } -/* We can always eliminate to the hard frame pointer. We can eliminate - to the stack pointer unless a frame pointer is needed. */ - -static bool -score_can_eliminate (const int from ATTRIBUTE_UNUSED, const int to) -{ - return (to == HARD_FRAME_POINTER_REGNUM - || (to == STACK_POINTER_REGNUM && !frame_pointer_needed)); -} - /* Implement INITIAL_ELIMINATION_OFFSET. FROM is either the frame pointer or argument pointer. TO is either the stack pointer or hard frame pointer. */ @@ -378,32 +852,29 @@ score_initial_elimination_offset (int from, int to ATTRIBUTE_UNUSED) { - if (TARGET_SCORE7 || TARGET_SCORE7D) - return score7_initial_elimination_offset (from, to); - else - gcc_unreachable (); + struct score_frame_info *f = score_compute_frame_size (get_frame_size ()); + switch (from) + { + case ARG_POINTER_REGNUM: + return f->total_size; + case FRAME_POINTER_REGNUM: + return 0; + default: + gcc_unreachable (); + } } -/* Argument support functions. */ - -/* Initialize CUMULATIVE_ARGS for a function. */ -void -score_init_cumulative_args (CUMULATIVE_ARGS *cum, - tree fntype ATTRIBUTE_UNUSED, - rtx libname ATTRIBUTE_UNUSED) -{ - memset (cum, 0, sizeof (CUMULATIVE_ARGS)); -} - /* Implement TARGET_FUNCTION_ARG_ADVANCE hook. */ static void score_function_arg_advance (CUMULATIVE_ARGS *cum, enum machine_mode mode, const_tree type, bool named) { - if (TARGET_SCORE7 || TARGET_SCORE7D) - score7_function_arg_advance (cum, mode, type, named); - else - gcc_unreachable (); + struct score_arg_info info; + score_classify_arg (cum, mode, type, named, &info); + cum->num_gprs = info.reg_offset + info.reg_words; + if (info.stack_words > 0) + cum->stack_words = info.stack_offset + info.stack_words; + cum->arg_number++; } /* Implement TARGET_ARG_PARTIAL_BYTES macro. */ @@ -411,87 +882,120 @@ score_arg_partial_bytes (CUMULATIVE_ARGS *cum, enum machine_mode mode, tree type, bool named) { - if (TARGET_SCORE7 || TARGET_SCORE7D) - return score7_arg_partial_bytes (cum, mode, type, named); - else - gcc_unreachable (); + struct score_arg_info info; + score_classify_arg (cum, mode, type, named, &info); + return info.stack_words > 0 ? info.reg_words * UNITS_PER_WORD : 0; } /* Implement TARGET_FUNCTION_ARG hook. */ static rtx -score_function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode, +score_function_arg (const CUMULATIVE_ARGS *cum, enum machine_mode mode, const_tree type, bool named) { - if (TARGET_SCORE7 || TARGET_SCORE7D) - return score7_function_arg (cum, mode, type, named); + struct score_arg_info info; + + if (mode == VOIDmode || !named) + return 0; + + score_classify_arg (cum, mode, type, named, &info); + + if (info.reg_offset == ARG_REG_NUM) + return 0; + + if (!info.stack_words) + return gen_rtx_REG (mode, ARG_REG_FIRST + info.reg_offset); else - gcc_unreachable (); + { + rtx ret = gen_rtx_PARALLEL (mode, rtvec_alloc (info.reg_words)); + unsigned int i, part_offset = 0; + for (i = 0; i < info.reg_words; i++) + { + rtx reg; + reg = gen_rtx_REG (SImode, ARG_REG_FIRST + info.reg_offset + i); + XVECEXP (ret, 0, i) = gen_rtx_EXPR_LIST (SImode, reg, + GEN_INT (part_offset)); + part_offset += UNITS_PER_WORD; + } + return ret; + } } /* Implement FUNCTION_VALUE and LIBCALL_VALUE. For normal calls, VALTYPE is the return type and MODE is VOIDmode. For libcalls, VALTYPE is null and MODE is the mode of the return value. */ rtx -score_function_value (const_tree valtype, const_tree func ATTRIBUTE_UNUSED, - enum machine_mode mode) +score_function_value (const_tree valtype, const_tree func, enum machine_mode mode) { - if (TARGET_SCORE7 || TARGET_SCORE7D) - return score7_function_value (valtype, func, mode); - else - gcc_unreachable (); + if (valtype) + { + int unsignedp; + mode = TYPE_MODE (valtype); + unsignedp = TYPE_UNSIGNED (valtype); + mode = promote_function_mode (valtype, mode, &unsignedp, func, 1); + } + return gen_rtx_REG (mode, RT_REGNUM); } /* Implement TARGET_ASM_TRAMPOLINE_TEMPLATE. */ + static void score_asm_trampoline_template (FILE *f) { - if (TARGET_SCORE7 || TARGET_SCORE7D) - score7_asm_trampoline_template (f); - else - gcc_unreachable (); + fprintf (f, "\t.set r1\n"); + fprintf (f, "\tmv r31, r3\n"); + fprintf (f, "\tbl nextinsn\n"); + fprintf (f, "nextinsn:\n"); + fprintf (f, "\tlw r1, [r3, 6*4-8]\n"); + fprintf (f, "\tlw r23, [r3, 6*4-4]\n"); + fprintf (f, "\tmv r3, r31\n"); + fprintf (f, "\tbr! r1\n"); + fprintf (f, "\tnop!\n"); + fprintf (f, "\t.set nor1\n"); } /* Implement TARGET_TRAMPOLINE_INIT. */ static void score_trampoline_init (rtx m_tramp, tree fndecl, rtx chain_value) { - if ( TARGET_SCORE7 || TARGET_SCORE7D) - score7_trampoline_init (m_tramp, fndecl, chain_value); - else - gcc_unreachable (); +#define CODE_SIZE (TRAMPOLINE_INSNS * UNITS_PER_WORD) + + 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, CODE_SIZE); + emit_move_insn (mem, fnaddr); + mem = adjust_address (m_tramp, SImode, CODE_SIZE + GET_MODE_SIZE (SImode)); + emit_move_insn (mem, chain_value); + +#undef CODE_SIZE } /* This function is used to implement REG_MODE_OK_FOR_BASE_P macro. */ int score_regno_mode_ok_for_base_p (int regno, int strict) { - if (TARGET_SCORE7 || TARGET_SCORE7D) - return score7_regno_mode_ok_for_base_p (regno, strict); - else - gcc_unreachable (); + if (regno >= FIRST_PSEUDO_REGISTER) + { + if (!strict) + return 1; + regno = reg_renumber[regno]; + } + if (regno == ARG_POINTER_REGNUM + || regno == FRAME_POINTER_REGNUM) + return 1; + return GP_REG_P (regno); } -/* Implement TARGET_LEGITIMIZE_ADDRESS_P. */ +/* Implement TARGET_LEGITIMATE_ADDRESS_P macro. */ static bool score_legitimate_address_p (enum machine_mode mode, rtx x, bool strict) { - if (TARGET_SCORE7 || TARGET_SCORE7D) - return score7_legitimate_address_p (mode, x, strict); - else - gcc_unreachable (); -} + struct score_address_info addr; -/* This function is used to implement LEGITIMIZE_ADDRESS. If X can - be legitimized in a way that the generic machinery might not expect, - return the new address, else return X. */ -static rtx -score_legitimize_address (rtx x, rtx oldx ATTRIBUTE_UNUSED, - enum machine_mode mode ATTRIBUTE_UNUSED) -{ - if (TARGET_SCORE7 || TARGET_SCORE7D) - return score7_legitimize_address (x); - else - gcc_unreachable (); + return score_classify_address (&addr, mode, x, strict); } /* Return a number assessing the cost of moving a register in class @@ -500,21 +1004,226 @@ score_register_move_cost (enum machine_mode mode ATTRIBUTE_UNUSED, enum reg_class from, enum reg_class to) { - if (TARGET_SCORE7 || TARGET_SCORE7D) - return score7_register_move_cost (mode, from, to); + if (GR_REG_CLASS_P (from)) + { + if (GR_REG_CLASS_P (to)) + return 2; + else if (SP_REG_CLASS_P (to)) + return 4; + else if (CP_REG_CLASS_P (to)) + return 5; + else if (CE_REG_CLASS_P (to)) + return 6; + } + if (GR_REG_CLASS_P (to)) + { + if (GR_REG_CLASS_P (from)) + return 2; + else if (SP_REG_CLASS_P (from)) + return 4; + else if (CP_REG_CLASS_P (from)) + return 5; + else if (CE_REG_CLASS_P (from)) + return 6; + } + return 12; +} + +/* Return the number of instructions needed to load a symbol of the + given type into a register. */ +static int +score_symbol_insns (enum score_symbol_type type) +{ + switch (type) + { + case SYMBOL_GENERAL: + return 2; + + case SYMBOL_SMALL_DATA: + return 1; + } + + gcc_unreachable (); +} + +/* Return the number of instructions needed to load or store a value + of mode MODE at X. Return 0 if X isn't valid for MODE. */ +static int +score_address_insns (rtx x, enum machine_mode mode) +{ + struct score_address_info addr; + int factor; + + if (mode == BLKmode) + factor = 1; else - gcc_unreachable (); + factor = (GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1) / UNITS_PER_WORD; + + if (score_classify_address (&addr, mode, x, false)) + switch (addr.type) + { + case SCORE_ADD_REG: + case SCORE_ADD_CONST_INT: + return factor; + + case SCORE_ADD_SYMBOLIC: + return factor * score_symbol_insns (addr.symbol_type); + } + return 0; } /* Implement TARGET_RTX_COSTS macro. */ bool -score_rtx_costs (rtx x, int code, int outer_code, int *total, - bool speed ATTRIBUTE_UNUSED) +score_rtx_costs (rtx x, int code, int outer_code, int *total, bool speed ATTRIBUTE_UNUSED) { - if (TARGET_SCORE7 || TARGET_SCORE7D) - return score7_rtx_costs (x, code, outer_code, total, speed); - else - gcc_unreachable (); + enum machine_mode mode = GET_MODE (x); + + switch (code) + { + case CONST_INT: + if (outer_code == SET) + { + if (((INTVAL (x) & 0xffff) == 0) + || (INTVAL (x) >= -32768 && INTVAL (x) <= 32767)) + *total = COSTS_N_INSNS (1); + else + *total = COSTS_N_INSNS (2); + } + else if (outer_code == PLUS || outer_code == MINUS) + { + if (INTVAL (x) >= -8192 && INTVAL (x) <= 8191) + *total = 0; + else if (((INTVAL (x) & 0xffff) == 0) + || (INTVAL (x) >= -32768 && INTVAL (x) <= 32767)) + *total = 1; + else + *total = COSTS_N_INSNS (2); + } + else if (outer_code == AND || outer_code == IOR) + { + if (INTVAL (x) >= 0 && INTVAL (x) <= 16383) + *total = 0; + else if (((INTVAL (x) & 0xffff) == 0) + || (INTVAL (x) >= 0 && INTVAL (x) <= 65535)) + *total = 1; + else + *total = COSTS_N_INSNS (2); + } + else + { + *total = 0; + } + return true; + + case CONST: + case SYMBOL_REF: + case LABEL_REF: + case CONST_DOUBLE: + *total = COSTS_N_INSNS (2); + return true; + + case MEM: + { + /* If the address is legitimate, return the number of + instructions it needs, otherwise use the default handling. */ + int n = score_address_insns (XEXP (x, 0), GET_MODE (x)); + if (n > 0) + { + *total = COSTS_N_INSNS (n + 1); + return true; + } + return false; + } + + case FFS: + *total = COSTS_N_INSNS (6); + return true; + + case NOT: + *total = COSTS_N_INSNS (1); + return true; + + case AND: + case IOR: + case XOR: + if (mode == DImode) + { + *total = COSTS_N_INSNS (2); + return true; + } + return false; + + case ASHIFT: + case ASHIFTRT: + case LSHIFTRT: + if (mode == DImode) + { + *total = COSTS_N_INSNS ((GET_CODE (XEXP (x, 1)) == CONST_INT) + ? 4 : 12); + return true; + } + return false; + + case ABS: + *total = COSTS_N_INSNS (4); + return true; + + case PLUS: + case MINUS: + if (mode == DImode) + { + *total = COSTS_N_INSNS (4); + return true; + } + *total = COSTS_N_INSNS (1); + return true; + + case NEG: + if (mode == DImode) + { + *total = COSTS_N_INSNS (4); + return true; + } + return false; + + case MULT: + *total = optimize_size ? COSTS_N_INSNS (2) : COSTS_N_INSNS (12); + return true; + + case DIV: + case MOD: + case UDIV: + case UMOD: + *total = optimize_size ? COSTS_N_INSNS (2) : COSTS_N_INSNS (33); + return true; + + case SIGN_EXTEND: + case ZERO_EXTEND: + switch (GET_MODE (XEXP (x, 0))) + { + case QImode: + case HImode: + if (GET_CODE (XEXP (x, 0)) == MEM) + { + *total = COSTS_N_INSNS (2); + + if (!TARGET_LITTLE_ENDIAN && + side_effects_p (XEXP (XEXP (x, 0), 0))) + *total = 100; + } + else + *total = COSTS_N_INSNS (1); + break; + + default: + *total = COSTS_N_INSNS (1); + break; + } + return true; + + default: + return false; + } } /* Implement TARGET_ADDRESS_COST macro. */ @@ -522,10 +1231,7 @@ score_address_cost (rtx addr, bool speed ATTRIBUTE_UNUSED) { - if (TARGET_SCORE7 || TARGET_SCORE7D) - return score7_address_cost (addr); - else - gcc_unreachable (); + return score_address_insns (addr, SImode); } /* Implement ASM_OUTPUT_EXTERNAL macro. */ @@ -533,10 +1239,17 @@ score_output_external (FILE *file ATTRIBUTE_UNUSED, tree decl, const char *name) { - if (TARGET_SCORE7 || TARGET_SCORE7D) - return score7_output_external (file, decl, name); - else - gcc_unreachable (); + register struct extern_list *p; + + if (score_in_small_data_p (decl)) + { + p = ggc_alloc_extern_list (); + p->next = extern_head; + p->name = name; + p->size = int_size_in_bytes (TREE_TYPE (decl)); + extern_head = p; + } + return 0; } /* Implement RETURN_ADDR_RTX. Note, we do not support moving @@ -544,40 +1257,363 @@ rtx score_return_addr (int count, rtx frame ATTRIBUTE_UNUSED) { - if (TARGET_SCORE7 || TARGET_SCORE7D) - return score7_return_addr (count, frame); - else - gcc_unreachable (); + if (count != 0) + return const0_rtx; + return get_hard_reg_initial_val (Pmode, RA_REGNUM); } /* Implement PRINT_OPERAND macro. */ +/* Score-specific operand codes: + '[' print .set nor1 directive + ']' print .set r1 directive + 'U' print hi part of a CONST_INT rtx + 'E' print log2(v) + 'F' print log2(~v) + 'D' print SFmode const double + 'S' selectively print "!" if operand is 15bit instruction accessible + 'V' print "v!" if operand is 15bit instruction accessible, or "lfh!" + 'L' low part of DImode reg operand + 'H' high part of DImode reg operand + 'C' print part of opcode for a branch condition. */ void score_print_operand (FILE *file, rtx op, int c) { - if (TARGET_SCORE7 || TARGET_SCORE7D) - score7_print_operand (file, op, c); + enum rtx_code code = UNKNOWN; + if (!PRINT_OPERAND_PUNCT_VALID_P (c)) + code = GET_CODE (op); + + if (c == '[') + { + fprintf (file, ".set r1\n"); + } + else if (c == ']') + { + fprintf (file, "\n\t.set nor1"); + } + else if (c == 'U') + { + gcc_assert (code == CONST_INT); + fprintf (file, HOST_WIDE_INT_PRINT_HEX, + (INTVAL (op) >> 16) & 0xffff); + } + else if (c == 'D') + { + if (GET_CODE (op) == CONST_DOUBLE) + { + rtx temp = gen_lowpart (SImode, op); + gcc_assert (GET_MODE (op) == SFmode); + fprintf (file, HOST_WIDE_INT_PRINT_HEX, INTVAL (temp) & 0xffffffff); + } + else + output_addr_const (file, op); + } + else if (c == 'S') + { + gcc_assert (code == REG); + if (G16_REG_P (REGNO (op))) + fprintf (file, "!"); + } + else if (c == 'V') + { + gcc_assert (code == REG); + fprintf (file, G16_REG_P (REGNO (op)) ? "v!" : "lfh!"); + } + else if (c == 'C') + { + enum machine_mode mode = GET_MODE (XEXP (op, 0)); + + switch (code) + { + case EQ: fputs ("eq", file); break; + case NE: fputs ("ne", file); break; + case GT: fputs ("gt", file); break; + case GE: fputs (mode != CCmode ? "pl" : "ge", file); break; + case LT: fputs (mode != CCmode ? "mi" : "lt", file); break; + case LE: fputs ("le", file); break; + case GTU: fputs ("gtu", file); break; + case GEU: fputs ("cs", file); break; + case LTU: fputs ("cc", file); break; + case LEU: fputs ("leu", file); break; + default: + output_operand_lossage ("invalid operand for code: '%c'", code); + } + } + else if (c == 'E') + { + unsigned HOST_WIDE_INT i; + unsigned HOST_WIDE_INT pow2mask = 1; + unsigned HOST_WIDE_INT val; + + val = INTVAL (op); + for (i = 0; i < 32; i++) + { + if (val == pow2mask) + break; + pow2mask <<= 1; + } + gcc_assert (i < 32); + fprintf (file, HOST_WIDE_INT_PRINT_HEX, i); + } + else if (c == 'F') + { + unsigned HOST_WIDE_INT i; + unsigned HOST_WIDE_INT pow2mask = 1; + unsigned HOST_WIDE_INT val; + + val = ~INTVAL (op); + for (i = 0; i < 32; i++) + { + if (val == pow2mask) + break; + pow2mask <<= 1; + } + gcc_assert (i < 32); + fprintf (file, HOST_WIDE_INT_PRINT_HEX, i); + } + else if (code == REG) + { + int regnum = REGNO (op); + if ((c == 'H' && !WORDS_BIG_ENDIAN) + || (c == 'L' && WORDS_BIG_ENDIAN)) + regnum ++; + fprintf (file, "%s", reg_names[regnum]); + } else - gcc_unreachable (); + { + switch (code) + { + case MEM: + score_print_operand_address (file, op); + break; + default: + output_addr_const (file, op); + } + } } /* Implement PRINT_OPERAND_ADDRESS macro. */ void score_print_operand_address (FILE *file, rtx x) { - if (TARGET_SCORE7 || TARGET_SCORE7D) - score7_print_operand_address (file, x); - else - gcc_unreachable (); + struct score_address_info addr; + enum rtx_code code = GET_CODE (x); + enum machine_mode mode = GET_MODE (x); + + if (code == MEM) + x = XEXP (x, 0); + + if (score_classify_address (&addr, mode, x, true)) + { + switch (addr.type) + { + case SCORE_ADD_REG: + { + switch (addr.code) + { + case PRE_DEC: + fprintf (file, "[%s,-%ld]+", reg_names[REGNO (addr.reg)], + INTVAL (addr.offset)); + break; + case POST_DEC: + fprintf (file, "[%s]+,-%ld", reg_names[REGNO (addr.reg)], + INTVAL (addr.offset)); + break; + case PRE_INC: + fprintf (file, "[%s, %ld]+", reg_names[REGNO (addr.reg)], + INTVAL (addr.offset)); + break; + case POST_INC: + fprintf (file, "[%s]+, %ld", reg_names[REGNO (addr.reg)], + INTVAL (addr.offset)); + break; + default: + if (INTVAL(addr.offset) == 0) + fprintf(file, "[%s]", reg_names[REGNO (addr.reg)]); + else + fprintf(file, "[%s, %ld]", reg_names[REGNO (addr.reg)], + INTVAL(addr.offset)); + break; + } + } + return; + case SCORE_ADD_CONST_INT: + case SCORE_ADD_SYMBOLIC: + output_addr_const (file, x); + return; + } + } + print_rtl (stderr, x); + gcc_unreachable (); } /* Implement SELECT_CC_MODE macro. */ enum machine_mode score_select_cc_mode (enum rtx_code op, rtx x, rtx y) { - if (TARGET_SCORE7 || TARGET_SCORE7D) - return score7_select_cc_mode (op, x, y); + if ((op == EQ || op == NE || op == LT || op == GE) + && y == const0_rtx + && GET_MODE (x) == SImode) + { + switch (GET_CODE (x)) + { + case PLUS: + case MINUS: + case NEG: + case AND: + case IOR: + case XOR: + case NOT: + case ASHIFT: + case LSHIFTRT: + case ASHIFTRT: + return CC_NZmode; + + case SIGN_EXTEND: + case ZERO_EXTEND: + case ROTATE: + case ROTATERT: + return (op == LT || op == GE) ? CC_Nmode : CCmode; + + default: + return CCmode; + } + } + + if ((op == EQ || op == NE) + && (GET_CODE (y) == NEG) + && register_operand (XEXP (y, 0), SImode) + && register_operand (x, SImode)) + { + return CC_NZmode; + } + + return CCmode; +} + +/* Generate the prologue instructions for entry into a S+core function. */ +void +score_prologue (void) +{ +#define EMIT_PL(_rtx) RTX_FRAME_RELATED_P (_rtx) = 1 + + struct score_frame_info *f = score_compute_frame_size (get_frame_size ()); + HOST_WIDE_INT size; + int regno; + + size = f->total_size - f->gp_reg_size; + + if (flag_pic) + emit_insn (gen_cpload_score7 ()); + + for (regno = (int) GP_REG_LAST; regno >= (int) GP_REG_FIRST; regno--) + { + if (BITSET_P (f->mask, regno - GP_REG_FIRST)) + { + rtx mem = gen_rtx_MEM (SImode, + gen_rtx_PRE_DEC (SImode, stack_pointer_rtx)); + rtx reg = gen_rtx_REG (SImode, regno); + if (!crtl->calls_eh_return) + MEM_READONLY_P (mem) = 1; + EMIT_PL (emit_insn (gen_pushsi_score7 (mem, reg))); + } + } + + if (size > 0) + { + rtx insn; + + if (size >= -32768 && size <= 32767) + EMIT_PL (emit_insn (gen_add3_insn (stack_pointer_rtx, + stack_pointer_rtx, + GEN_INT (-size)))); + else + { + EMIT_PL (emit_move_insn (gen_rtx_REG (Pmode, SCORE_PROLOGUE_TEMP_REGNUM), + GEN_INT (size))); + EMIT_PL (emit_insn + (gen_sub3_insn (stack_pointer_rtx, + stack_pointer_rtx, + gen_rtx_REG (Pmode, + SCORE_PROLOGUE_TEMP_REGNUM)))); + } + insn = get_last_insn (); + REG_NOTES (insn) = + alloc_EXPR_LIST (REG_FRAME_RELATED_EXPR, + gen_rtx_SET (VOIDmode, stack_pointer_rtx, + plus_constant (stack_pointer_rtx, + -size)), + REG_NOTES (insn)); + } + + if (frame_pointer_needed) + EMIT_PL (emit_move_insn (hard_frame_pointer_rtx, stack_pointer_rtx)); + + if (flag_pic && f->cprestore_size) + { + if (frame_pointer_needed) + emit_insn (gen_cprestore_use_fp_score7 (GEN_INT (size - f->cprestore_size))); + else + emit_insn (gen_cprestore_use_sp_score7 (GEN_INT (size - f->cprestore_size))); + } + +#undef EMIT_PL +} + +/* Generate the epilogue instructions in a S+core function. */ +void +score_epilogue (int sibcall_p) +{ + struct score_frame_info *f = score_compute_frame_size (get_frame_size ()); + HOST_WIDE_INT size; + int regno; + rtx base; + + size = f->total_size - f->gp_reg_size; + + if (!frame_pointer_needed) + base = stack_pointer_rtx; else - gcc_unreachable (); + base = hard_frame_pointer_rtx; + + if (size) + { + if (size >= -32768 && size <= 32767) + emit_insn (gen_add3_insn (base, base, GEN_INT (size))); + else + { + emit_move_insn (gen_rtx_REG (Pmode, SCORE_EPILOGUE_TEMP_REGNUM), + GEN_INT (size)); + emit_insn (gen_add3_insn (base, base, + gen_rtx_REG (Pmode, + SCORE_EPILOGUE_TEMP_REGNUM))); + } + } + + if (base != stack_pointer_rtx) + emit_move_insn (stack_pointer_rtx, base); + + if (crtl->calls_eh_return) + emit_insn (gen_add3_insn (stack_pointer_rtx, + stack_pointer_rtx, + EH_RETURN_STACKADJ_RTX)); + + for (regno = (int) GP_REG_FIRST; regno <= (int) GP_REG_LAST; regno++) + { + if (BITSET_P (f->mask, regno - GP_REG_FIRST)) + { + rtx mem = gen_rtx_MEM (SImode, + gen_rtx_POST_INC (SImode, stack_pointer_rtx)); + rtx reg = gen_rtx_REG (SImode, regno); + + if (!crtl->calls_eh_return) + MEM_READONLY_P (mem) = 1; + + emit_insn (gen_popsi_score7 (reg, mem)); + } + } + + if (!sibcall_p) + emit_jump_insn (gen_return_internal_score7 (gen_rtx_REG (Pmode, RA_REGNUM))); } /* Return true if X is a symbolic constant that can be calculated in @@ -586,143 +1622,406 @@ int score_symbolic_constant_p (rtx x, enum score_symbol_type *symbol_type) { - if (TARGET_SCORE7 || TARGET_SCORE7D) - return score7_symbolic_constant_p (x, symbol_type); + HOST_WIDE_INT offset; + + score_split_const (x, &x, &offset); + if (GET_CODE (x) == SYMBOL_REF || GET_CODE (x) == LABEL_REF) + *symbol_type = score_classify_symbol (x); else - gcc_unreachable (); + return 0; + + if (offset == 0) + return 1; + + /* if offset > 15bit, must reload */ + if (!IMM_IN_RANGE (offset, 15, 1)) + return 0; + + switch (*symbol_type) + { + case SYMBOL_GENERAL: + return 1; + case SYMBOL_SMALL_DATA: + return score_offset_within_object_p (x, offset); + } + gcc_unreachable (); } -/* Generate the prologue instructions for entry into a S+core function. */ void -score_prologue (void) +score_movsicc (rtx *ops) { - if (TARGET_SCORE7 || TARGET_SCORE7D) - score7_prologue (); - else - gcc_unreachable (); -} + enum machine_mode mode; -/* Generate the epilogue instructions in a S+core function. */ -void -score_epilogue (int sibcall_p) -{ - if (TARGET_SCORE7 || TARGET_SCORE7D) - score7_epilogue (sibcall_p); - else - gcc_unreachable (); + mode = score_select_cc_mode (GET_CODE (ops[1]), ops[2], ops[3]); + emit_insn (gen_rtx_SET (VOIDmode, gen_rtx_REG (mode, CC_REGNUM), + gen_rtx_COMPARE (mode, XEXP (ops[1], 0), + XEXP (ops[1], 1)))); } /* Call and sibcall pattern all need call this function. */ void score_call (rtx *ops, bool sib) { - if (TARGET_SCORE7 || TARGET_SCORE7D) - score7_call (ops, sib); + rtx addr = XEXP (ops[0], 0); + if (!call_insn_operand (addr, VOIDmode)) + { + rtx oaddr = addr; + addr = gen_reg_rtx (Pmode); + gen_move_insn (addr, oaddr); + } + + if (sib) + emit_call_insn (gen_sibcall_internal_score7 (addr, ops[1])); else - gcc_unreachable (); + emit_call_insn (gen_call_internal_score7 (addr, ops[1])); } /* Call value and sibcall value pattern all need call this function. */ void score_call_value (rtx *ops, bool sib) { - if (TARGET_SCORE7 || TARGET_SCORE7D) - score7_call_value (ops, sib); - else - gcc_unreachable (); -} + rtx result = ops[0]; + rtx addr = XEXP (ops[1], 0); + rtx arg = ops[2]; -void -score_movsicc (rtx *ops) -{ - if (TARGET_SCORE7 || TARGET_SCORE7D) - score7_movsicc (ops); + if (!call_insn_operand (addr, VOIDmode)) + { + rtx oaddr = addr; + addr = gen_reg_rtx (Pmode); + gen_move_insn (addr, oaddr); + } + + if (sib) + emit_call_insn (gen_sibcall_value_internal_score7 (result, addr, arg)); else - gcc_unreachable (); + emit_call_insn (gen_call_value_internal_score7 (result, addr, arg)); } /* Machine Split */ void score_movdi (rtx *ops) { - if (TARGET_SCORE7 || TARGET_SCORE7D) - score7_movdi (ops); + rtx dst = ops[0]; + rtx src = ops[1]; + rtx dst0 = score_subw (dst, 0); + rtx dst1 = score_subw (dst, 1); + rtx src0 = score_subw (src, 0); + rtx src1 = score_subw (src, 1); + + if (GET_CODE (dst0) == REG && reg_overlap_mentioned_p (dst0, src)) + { + emit_move_insn (dst1, src1); + emit_move_insn (dst0, src0); + } else - gcc_unreachable (); + { + emit_move_insn (dst0, src0); + emit_move_insn (dst1, src1); + } } void score_zero_extract_andi (rtx *ops) { - if (TARGET_SCORE7 || TARGET_SCORE7D) - score7_zero_extract_andi (ops); + if (INTVAL (ops[1]) == 1 && const_uimm5 (ops[2], SImode)) + emit_insn (gen_zero_extract_bittst_score7 (ops[0], ops[2])); else - gcc_unreachable (); + { + unsigned HOST_WIDE_INT mask; + mask = (0xffffffffU & ((1U << INTVAL (ops[1])) - 1U)); + mask = mask << INTVAL (ops[2]); + emit_insn (gen_andsi3_cmp_score7 (ops[3], ops[0], + gen_int_mode (mask, SImode))); + } } -/* Output asm insn for move. */ -const char * -score_move (rtx *ops) +/* Check addr could be present as PRE/POST mode. */ +static bool +score_pindex_mem (rtx addr) { - if (TARGET_SCORE7 || TARGET_SCORE7D) - return score7_move (ops); - else - gcc_unreachable (); + if (GET_CODE (addr) == MEM) + { + switch (GET_CODE (XEXP (addr, 0))) + { + case PRE_DEC: + case POST_DEC: + case PRE_INC: + case POST_INC: + return true; + default: + break; + } + } + return false; } +/* Output asm code for ld/sw insn. */ +static int +score_pr_addr_post (rtx *ops, int idata, int iaddr, char *ip, enum score_mem_unit unit) +{ + struct score_address_info ai; + + gcc_assert (GET_CODE (ops[idata]) == REG); + gcc_assert (score_classify_address (&ai, SImode, XEXP (ops[iaddr], 0), true)); + + if (!score_pindex_mem (ops[iaddr]) + && ai.type == SCORE_ADD_REG + && GET_CODE (ai.offset) == CONST_INT + && G16_REG_P (REGNO (ops[idata])) + && G16_REG_P (REGNO (ai.reg))) + { + if (INTVAL (ai.offset) == 0) + { + ops[iaddr] = ai.reg; + return snprintf (ip, INS_BUF_SZ, + "!\t%%%d, [%%%d]", idata, iaddr); + } + if (REGNO (ai.reg) == HARD_FRAME_POINTER_REGNUM) + { + HOST_WIDE_INT offset = INTVAL (ai.offset); + if (SCORE_ALIGN_UNIT (offset, unit) + && (((offset >> unit) >= 0) && ((offset >> unit) <= 31))) + { + ops[iaddr] = ai.offset; + return snprintf (ip, INS_BUF_SZ, + "p!\t%%%d, %%c%d", idata, iaddr); + } + } + } + return snprintf (ip, INS_BUF_SZ, "\t%%%d, %%a%d", idata, iaddr); +} + /* Output asm insn for load. */ const char * score_linsn (rtx *ops, enum score_mem_unit unit, bool sign) { - if (TARGET_SCORE7 || TARGET_SCORE7D) - return score7_linsn (ops, unit, sign); + const char *pre_ins[] = + {"lbu", "lhu", "lw", "??", "lb", "lh", "lw", "??"}; + char *ip; + + strcpy (score_ins, pre_ins[(sign ? 4 : 0) + unit]); + ip = score_ins + strlen (score_ins); + + if ((!sign && unit != SCORE_HWORD) + || (sign && unit != SCORE_BYTE)) + score_pr_addr_post (ops, 0, 1, ip, unit); else - gcc_unreachable (); + snprintf (ip, INS_BUF_SZ, "\t%%0, %%a1"); + + return score_ins; } /* Output asm insn for store. */ const char * score_sinsn (rtx *ops, enum score_mem_unit unit) { - if (TARGET_SCORE7 || TARGET_SCORE7D) - return score7_sinsn (ops, unit); - else - gcc_unreachable (); + const char *pre_ins[] = {"sb", "sh", "sw"}; + char *ip; + + strcpy (score_ins, pre_ins[unit]); + ip = score_ins + strlen (score_ins); + score_pr_addr_post (ops, 1, 0, ip, unit); + return score_ins; } /* Output asm insn for load immediate. */ const char * score_limm (rtx *ops) { - if (TARGET_SCORE7 || TARGET_SCORE7D) - return score7_limm (ops); + HOST_WIDE_INT v; + + gcc_assert (GET_CODE (ops[0]) == REG); + gcc_assert (GET_CODE (ops[1]) == CONST_INT); + + v = INTVAL (ops[1]); + if (G16_REG_P (REGNO (ops[0])) && IMM_IN_RANGE (v, 8, 0)) + return "ldiu!\t%0, %c1"; + else if (IMM_IN_RANGE (v, 16, 1)) + return "ldi\t%0, %c1"; + else if ((v & 0xffff) == 0) + return "ldis\t%0, %U1"; else - gcc_unreachable (); + return "li\t%0, %c1"; } +/* Output asm insn for move. */ +const char * +score_move (rtx *ops) +{ + gcc_assert (GET_CODE (ops[0]) == REG); + gcc_assert (GET_CODE (ops[1]) == REG); + if (G16_REG_P (REGNO (ops[0]))) + { + if (G16_REG_P (REGNO (ops[1]))) + return "mv!\t%0, %1"; + else + return "mlfh!\t%0, %1"; + } + else if (G16_REG_P (REGNO (ops[1]))) + return "mhfl!\t%0, %1"; + else + return "mv\t%0, %1"; +} + /* Generate add insn. */ const char * score_select_add_imm (rtx *ops, bool set_cc) { - if (TARGET_SCORE7 || TARGET_SCORE7D) - return score7_select_add_imm (ops, set_cc); + HOST_WIDE_INT v = INTVAL (ops[2]); + + gcc_assert (GET_CODE (ops[2]) == CONST_INT); + gcc_assert (REGNO (ops[0]) == REGNO (ops[1])); + + if (set_cc && G16_REG_P (REGNO (ops[0]))) + { + if (v > 0 && IMM_IS_POW_OF_2 ((unsigned HOST_WIDE_INT) v, 0, 15)) + { + ops[2] = GEN_INT (ffs (v) - 1); + return "addei!\t%0, %c2"; + } + + if (v < 0 && IMM_IS_POW_OF_2 ((unsigned HOST_WIDE_INT) (-v), 0, 15)) + { + ops[2] = GEN_INT (ffs (-v) - 1); + return "subei!\t%0, %c2"; + } + } + + if (set_cc) + return "addi.c\t%0, %c2"; else - gcc_unreachable (); + return "addi\t%0, %c2"; } /* Output arith insn. */ const char * score_select (rtx *ops, const char *inst_pre, - bool commu, const char *letter, bool set_cc) + bool commu, const char *letter, bool set_cc) { - if (TARGET_SCORE7 || TARGET_SCORE7D) - return score7_select (ops, inst_pre, commu, letter, set_cc); + gcc_assert (GET_CODE (ops[0]) == REG); + gcc_assert (GET_CODE (ops[1]) == REG); + + if (set_cc && G16_REG_P (REGNO (ops[0])) + && (GET_CODE (ops[2]) == REG ? G16_REG_P (REGNO (ops[2])) : 1) + && REGNO (ops[0]) == REGNO (ops[1])) + { + snprintf (score_ins, INS_BUF_SZ, "%s!\t%%0, %%%s2", inst_pre, letter); + return score_ins; + } + + if (commu && set_cc && G16_REG_P (REGNO (ops[0])) + && G16_REG_P (REGNO (ops[1])) + && REGNO (ops[0]) == REGNO (ops[2])) + { + gcc_assert (GET_CODE (ops[2]) == REG); + snprintf (score_ins, INS_BUF_SZ, "%s!\t%%0, %%%s1", inst_pre, letter); + return score_ins; + } + + if (set_cc) + snprintf (score_ins, INS_BUF_SZ, "%s.c\t%%0, %%1, %%%s2", inst_pre, letter); else - gcc_unreachable (); + snprintf (score_ins, INS_BUF_SZ, "%s\t%%0, %%1, %%%s2", inst_pre, letter); + return score_ins; } +/* Return nonzero when an argument must be passed by reference. */ +static bool +score_pass_by_reference (CUMULATIVE_ARGS *cum ATTRIBUTE_UNUSED, + enum machine_mode mode, const_tree type, + bool named ATTRIBUTE_UNUSED) +{ + /* If we have a variable-sized parameter, we have no choice. */ + return targetm.calls.must_pass_in_stack (mode, type); +} + +/* Implement TARGET_FUNCTION_OK_FOR_SIBCALL. */ +static bool +score_function_ok_for_sibcall (ATTRIBUTE_UNUSED tree decl, + ATTRIBUTE_UNUSED tree exp) +{ + return true; +} + +/* Implement TARGET_SCHED_ISSUE_RATE. */ +static int +score_issue_rate (void) +{ + return 1; +} + +/* Implement TARGET_ASM_FILE_START. */ static void +score_asm_file_start (void) +{ + if (TARGET_SCORE7D) + fprintf (asm_out_file, "# Sunplus S+core7d %s rev=%s\n", + TARGET_LITTLE_ENDIAN ? "el" : "eb", SCORE_GCC_VERSION); + else if (TARGET_SCORE7) + fprintf (asm_out_file, "# Sunplus S+core7 %s rev=%s\n", + TARGET_LITTLE_ENDIAN ? "el" : "eb", SCORE_GCC_VERSION); + else + fprintf (asm_out_file, "# Sunplus S+core unknown %s rev=%s\n", + TARGET_LITTLE_ENDIAN ? "el" : "eb", SCORE_GCC_VERSION); + + default_file_start (); + + if (flag_pic) + fprintf (asm_out_file, "\t.set pic\n"); +} + +#define MASK_ALL_CPU_BITS (MASK_SCORE7 | MASK_SCORE7D) + +/* Implement TARGET_HANDLE_OPTION. */ +static bool +score_handle_option (struct gcc_options *opts, + struct gcc_options *opts_set ATTRIBUTE_UNUSED, + const struct cl_decoded_option *decoded, + location_t loc ATTRIBUTE_UNUSED) +{ + size_t code = decoded->opt_index; + int value = decoded->value; + + switch (code) + { + case OPT_mscore7d: + opts->x_target_flags &= ~(MASK_ALL_CPU_BITS); + opts->x_target_flags |= MASK_SCORE7 | MASK_SCORE7D; + return true; + + case OPT_march_: + opts->x_target_flags &= ~(MASK_ALL_CPU_BITS); + opts->x_target_flags |= value; + return true; + + default: + return true; + } +} + +/* We can always eliminate to the hard frame pointer. We can eliminate + to the stack pointer unless a frame pointer is needed. */ + +static bool +score_can_eliminate (const int from ATTRIBUTE_UNUSED, const int to) +{ + return (to == HARD_FRAME_POINTER_REGNUM + || (to == STACK_POINTER_REGNUM && !frame_pointer_needed)); +} + +/* Argument support functions. */ + +/* Initialize CUMULATIVE_ARGS for a function. */ +void +score_init_cumulative_args (CUMULATIVE_ARGS *cum, + tree fntype ATTRIBUTE_UNUSED, + rtx libname ATTRIBUTE_UNUSED) +{ + memset (cum, 0, sizeof (CUMULATIVE_ARGS)); +} + +static void score_conditional_register_usage (void) { if (!flag_pic)