diff options
author | jayants <jayants@138bc75d-0d04-0410-961f-82ee72b054a4> | 2012-02-02 10:20:32 +0000 |
---|---|---|
committer | jayants <jayants@138bc75d-0d04-0410-961f-82ee72b054a4> | 2012-02-02 10:20:32 +0000 |
commit | b9fdd12b8db041ca3d5d0bc23491fdc6db86f484 (patch) | |
tree | 6c6a242bece77442e5ca1c66403d5cee85d5584f /gcc/config/cr16 | |
parent | 80b67e55d6cbd8abbe5dbb0087eeea0dc3c27941 (diff) | |
download | gcc-b9fdd12b8db041ca3d5d0bc23491fdc6db86f484.tar.gz |
Adding GCC port for National Semicondutor's CR16 architecture
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@183832 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'gcc/config/cr16')
-rw-r--r-- | gcc/config/cr16/constraints.md | 81 | ||||
-rw-r--r-- | gcc/config/cr16/cr16-protos.h | 101 | ||||
-rw-r--r-- | gcc/config/cr16/cr16.c | 2190 | ||||
-rw-r--r-- | gcc/config/cr16/cr16.h | 590 | ||||
-rw-r--r-- | gcc/config/cr16/cr16.md | 1084 | ||||
-rw-r--r-- | gcc/config/cr16/cr16.opt | 51 | ||||
-rw-r--r-- | gcc/config/cr16/predicates.md | 225 | ||||
-rw-r--r-- | gcc/config/cr16/t-cr16 | 25 |
8 files changed, 4347 insertions, 0 deletions
diff --git a/gcc/config/cr16/constraints.md b/gcc/config/cr16/constraints.md new file mode 100644 index 00000000000..0e6e1120ff5 --- /dev/null +++ b/gcc/config/cr16/constraints.md @@ -0,0 +1,81 @@ +;; Predicates of machine description for CR16. +;; Copyright (C) 2012 Free Software Foundation, Inc. +;; Contributed by KPIT Cummins Infosystems Limited. +;; +;; This file is part of GCC. +;; +;; GCC is free software; you can redistribute it and/or modify it +;; under the terms of the GNU General Public License as published +;; by the Free Software Foundation; either version 3, or (at your +;; option) any later version. +;; +;; GCC is distributed in the hope that it will be useful, but WITHOUT +;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +;; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +;; License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with GCC; see the file COPYING3. If not see +;; <http://www.gnu.org/licenses/>. + +;; Constraints +;; Register constraints +(define_register_constraint "b" "NOSP_REGS" + "@no sp registers") + +(define_register_constraint "c" "SHORT_REGS" + "@short registers") + +(define_register_constraint "d" "LONG_REGS" + "@long registers") + +;; Integer constraints. +(define_constraint "I" + "A signed 4-bit immediate." + (and (match_code "const_int") + (match_test "SIGNED_INT_FITS_N_BITS (ival, 4)"))) + +(define_constraint "J" + "A signed 5-bit immediate." + (and (match_code "const_int") + (match_test "SIGNED_INT_FITS_N_BITS (ival, 5)"))) + +(define_constraint "K" + "A signed 6-bit immediate." + (and (match_code "const_int") + (match_test "SIGNED_INT_FITS_N_BITS (ival, 6)"))) + +(define_constraint "L" + "A unsigned 4-bit immediate." + (and (match_code "const_int") + (match_test "UNSIGNED_INT_FITS_N_BITS (ival, 4)"))) + +(define_constraint "M" + "A unsigned and customized 4-bit immediate." + (and (match_code "const_int") + (match_test "(IN_RANGE_P (ival, 0, 15) && ((ival != 9) && (ival != 11)))"))) + +(define_constraint "N" + "A signed 16-bit immediate." + (and (match_code "const_int") + (match_test "IN_RANGE_P (ival, -32768, 32767)"))) + +(define_constraint "O" + "A unsigned 20-bit immediate." + (and (match_code "const_int") + (match_test "IN_RANGE_P (ival, 0, 1048575)"))) + +(define_constraint "Q" + "A shift QI immediate." + (and (match_code "const_int") + (match_test "IN_RANGE_P (ival, 0, 7)"))) + +(define_constraint "R" + "A shift HI immediate." + (and (match_code "const_int") + (match_test "IN_RANGE_P (ival, 0, 15)"))) + +(define_constraint "S" + "A shift SI immediate." + (and (match_code "const_int") + (match_test "IN_RANGE_P (ival, 0, 31)"))) diff --git a/gcc/config/cr16/cr16-protos.h b/gcc/config/cr16/cr16-protos.h new file mode 100644 index 00000000000..882fc614633 --- /dev/null +++ b/gcc/config/cr16/cr16-protos.h @@ -0,0 +1,101 @@ +/* Prototypes for exported functions defined in cr16.c + Copyright (C) 2012 Free Software Foundation, Inc. + Contributed by KPIT Cummins Infosystems Limited. + + This file is part of GCC. + + GCC is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 3, or (at your + option) any later version. + + GCC is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + License for more details. + + You should have received a copy of the GNU General Public License + along with GCC; see the file COPYING3. If not see + <http://www.gnu.org/licenses/>. */ + +#ifndef GCC_CR16_PROTOS_H +#define GCC_CR16_PROTOS_H + +#include "target.h" + +/* Register usage. */ +extern enum reg_class cr16_regno_reg_class (int); +extern int cr16_hard_regno_mode_ok (int regno, enum machine_mode); + +/* Passing function arguments. */ +extern int cr16_function_arg_regno_p (int); + +#ifdef TREE_CODE +#ifdef RTX_CODE + +extern void cr16_init_cumulative_args (CUMULATIVE_ARGS *, tree, rtx); + +#endif /* RTX_CODE. */ +#endif /* TREE_CODE. */ + +/* Enumeration giving the various data models we support. */ +enum data_model_type +{ + DM_DEFAULT, /* Default data model (in CR16C/C+ - up to 16M). */ + DM_NEAR, /* Near data model (in CR16C/C+ - up to 1M). */ + DM_FAR, /* Far data model (in CR16C+ - up to 4G) + (in CR16C - up to 16M). */ + ILLEGAL_DM /* Illegal data model. */ +}; + +#ifdef RTX_CODE + +/* Addressing Modes. */ +struct cr16_address +{ + rtx base; /* Base register: Any register or register pair. */ + rtx index; /* Index register: If one is present. */ + rtx disp; /* Displacement or Absolute address. */ + enum data_model_type data; /* data ref type. */ + int code; /* Whether the address is code address. + 0 - data, 1 - code label, 2 - function label. */ +}; + +enum cr16_addrtype +{ + CR16_INVALID, + CR16_REG_REL, + CR16_REGP_REL, + CR16_INDEX_REGP_REL, + CR16_ABSOLUTE +}; + +extern void notice_update_cc PARAMS ((rtx)); +extern int cr16_operand_bit_pos (int val, int bitval); +extern void cr16_decompose_const (rtx x, int *code, + enum data_model_type *data, + bool treat_as_const); +extern enum cr16_addrtype cr16_decompose_address (rtx addr, + struct cr16_address *out, + bool debug_print, + bool treat_as_const); +extern int cr16_const_double_ok (rtx op); +extern int legitimate_pic_operand_p (rtx); +extern rtx legitimize_pic_address (rtx, enum machine_mode, rtx); + +#endif /* RTX_CODE. */ + + +/* Prologue/Epilogue functions. */ +extern int cr16_initial_elimination_offset (int, int); +extern char *cr16_prepare_push_pop_string (int); +extern void cr16_expand_prologue (void); +extern void cr16_expand_epilogue (void); +extern const char *cr16_emit_add_sub_di (rtx *, enum rtx_code); +extern const char *cr16_emit_logical_di (rtx *, enum rtx_code); + +/* Handling the "interrupt" attribute. */ +extern int cr16_interrupt_function_p (void); +extern bool cr16_is_data_model (enum data_model_type); + +#endif /* Not GCC_CR16_PROTOS_H. */ diff --git a/gcc/config/cr16/cr16.c b/gcc/config/cr16/cr16.c new file mode 100644 index 00000000000..65968f8cda7 --- /dev/null +++ b/gcc/config/cr16/cr16.c @@ -0,0 +1,2190 @@ +/* Output routines for CR16 processor. + Copyright (C) 2012 Free Software Foundation, Inc. + Contributed by KPIT Cummins Infosystems Limited. + + This file is part of GCC. + + GCC is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 3, or (at your + option) any later version. + + GCC is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + License for more details. + + You should have received a copy of the GNU General Public License + along with GCC; see the file COPYING3. If not see + <http://www.gnu.org/licenses/>. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tm.h" +#include "rtl.h" +#include "tree.h" +#include "tm_p.h" +#include "regs.h" +#include "hard-reg-set.h" +#include "insn-config.h" +#include "conditions.h" +#include "output.h" +#include "insn-codes.h" +#include "insn-attr.h" +#include "flags.h" +#include "except.h" +#include "function.h" +#include "recog.h" +#include "expr.h" +#include "optabs.h" +#include "diagnostic-core.h" +#include "basic-block.h" +#include "target.h" +#include "target-def.h" +#include "df.h" + +/* Definitions. */ + +/* Maximum number of register used for passing parameters. */ +#define MAX_REG_FOR_PASSING_ARGS 6 + +/* Minimum number register used for passing parameters. */ +#define MIN_REG_FOR_PASSING_ARGS 2 + +/* The maximum count of words supported in the assembly of the architecture in + a push/pop instruction. */ +#define MAX_COUNT 8 + +/* Predicate is true if the current function is a 'noreturn' function, + i.e. it is qualified as volatile. */ +#define FUNC_IS_NORETURN_P(decl) (TREE_THIS_VOLATILE (decl)) + +/* Predicate that holds when we need to save registers even for 'noreturn' + functions, to accomodate for unwinding. */ +#define MUST_SAVE_REGS_P() \ + (flag_unwind_tables || (flag_exceptions && !UI_SJLJ)) + +/* Nonzero if the rtx X is a signed const int of n bits. */ +#define RTX_SIGNED_INT_FITS_N_BITS(X, n) \ + ((GET_CODE (X) == CONST_INT \ + && SIGNED_INT_FITS_N_BITS (INTVAL (X), n)) ? 1 : 0) + +/* Nonzero if the rtx X is an unsigned const int of n bits. */ +#define RTX_UNSIGNED_INT_FITS_N_BITS(X, n) \ + ((GET_CODE (X) == CONST_INT \ + && UNSIGNED_INT_FITS_N_BITS (INTVAL (X), n)) ? 1 : 0) + +/* Structure for stack computations. */ + +/* variable definitions in the struture + args_size Number of bytes saved on the stack for local + variables + + reg_size Number of bytes saved on the stack for + non-scratch registers + + total_size The sum of 2 sizes: locals vars and padding byte + for saving the registers. Used in expand_prologue() + and expand_epilogue() + + last_reg_to_save Will hold the number of the last register the + prologue saves, -1 if no register is saved + + save_regs[16] Each object in the array is a register number. + Mark 1 for registers that need to be saved + + num_regs Number of registers saved + + initialized Non-zero if frame size already calculated, not + used yet + + function_makes_calls Does the function make calls ? not used yet. */ + +struct cr16_frame_info +{ + unsigned long var_size; + unsigned long args_size; + unsigned int reg_size; + unsigned long total_size; + long last_reg_to_save; + long save_regs[FIRST_PSEUDO_REGISTER]; + int num_regs; + int initialized; + int function_makes_calls; +}; + +/* Current frame information calculated by cr16_compute_frame_size. */ +static struct cr16_frame_info current_frame_info; + +/* Static Variables. */ + +/* Data model that was supplied by user via command line option + This will be overridden in case of invalid combination + of core and data model options are supplied. */ +static enum data_model_type data_model = DM_DEFAULT; + +/* TARGETM Function Prototypes and forward declarations */ +static void cr16_print_operand (FILE *, rtx, int); +static void cr16_print_operand_address (FILE *, rtx); + +/* Stack layout and calling conventions. */ +#undef TARGET_STRUCT_VALUE_RTX +#define TARGET_STRUCT_VALUE_RTX cr16_struct_value_rtx +#undef TARGET_RETURN_IN_MEMORY +#define TARGET_RETURN_IN_MEMORY cr16_return_in_memory + +/* Target-specific uses of '__attribute__'. */ +#undef TARGET_ATTRIBUTE_TABLE +#define TARGET_ATTRIBUTE_TABLE cr16_attribute_table +#undef TARGET_NARROW_VOLATILE_BITFIELD +#define TARGET_NARROW_VOLATILE_BITFIELD hook_bool_void_false + +/* EH related. */ +#undef TARGET_UNWIND_WORD_MODE +#define TARGET_UNWIND_WORD_MODE cr16_unwind_word_mode + +/* Override Options. */ +#undef TARGET_OPTION_OVERRIDE +#define TARGET_OPTION_OVERRIDE cr16_override_options + +/* Conditional register usuage. */ +#undef TARGET_CONDITIONAL_REGISTER_USAGE +#define TARGET_CONDITIONAL_REGISTER_USAGE cr16_conditional_register_usage + +/* Controlling register spills. */ +#undef TARGET_CLASS_LIKELY_SPILLED_P +#define TARGET_CLASS_LIKELY_SPILLED_P cr16_class_likely_spilled_p + +/* Passing function arguments. */ +#undef TARGET_FUNCTION_ARG +#define TARGET_FUNCTION_ARG cr16_function_arg +#undef TARGET_FUNCTION_ARG_ADVANCE +#define TARGET_FUNCTION_ARG_ADVANCE cr16_function_arg_advance +#undef TARGET_RETURN_POPS_ARGS +#define TARGET_RETURN_POPS_ARGS cr16_return_pops_args + +/* Initialize the GCC target structure. */ +#undef TARGET_FRAME_POINTER_REQUIRED +#define TARGET_FRAME_POINTER_REQUIRED cr16_frame_pointer_required +#undef TARGET_CAN_ELIMINATE +#define TARGET_CAN_ELIMINATE cr16_can_eliminate +#undef TARGET_LEGITIMIZE_ADDRESS +#define TARGET_LEGITIMIZE_ADDRESS cr16_legitimize_address +#undef TARGET_LEGITIMATE_CONSTANT_P +#define TARGET_LEGITIMATE_CONSTANT_P cr16_legitimate_constant_p +#undef TARGET_LEGITIMATE_ADDRESS_P +#define TARGET_LEGITIMATE_ADDRESS_P cr16_legitimate_address_p + +/* Returning function value. */ +#undef TARGET_FUNCTION_VALUE +#define TARGET_FUNCTION_VALUE cr16_function_value +#undef TARGET_LIBCALL_VALUE +#define TARGET_LIBCALL_VALUE cr16_libcall_value +#undef TARGET_FUNCTION_VALUE_REGNO_P +#define TARGET_FUNCTION_VALUE_REGNO_P cr16_function_value_regno_p + +/* printing the values. */ +#undef TARGET_PRINT_OPERAND +#define TARGET_PRINT_OPERAND cr16_print_operand +#undef TARGET_PRINT_OPERAND_ADDRESS +#define TARGET_PRINT_OPERAND_ADDRESS cr16_print_operand_address + +/* Relative costs of operations. */ +#undef TARGET_ADDRESS_COST +#define TARGET_ADDRESS_COST cr16_address_cost +#undef TARGET_REGISTER_MOVE_COST +#define TARGET_REGISTER_MOVE_COST cr16_register_move_cost +#undef TARGET_MEMORY_MOVE_COST +#define TARGET_MEMORY_MOVE_COST cr16_memory_move_cost + +/* Table of machine attributes. */ +static const struct attribute_spec cr16_attribute_table[] = { + /* ISRs have special prologue and epilogue requirements. */ + /* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler, + affects_type_identity }. */ + {"interrupt", 0, 0, false, true, true, NULL, false}, + {NULL, 0, 0, false, false, false, NULL, false} +}; + +/* TARGET_ASM_UNALIGNED_xx_OP generates .?byte directive + .?byte directive along with @c is not understood by assembler. + Therefore, make all TARGET_ASM_UNALIGNED_xx_OP same + as TARGET_ASM_ALIGNED_xx_OP. */ +#undef TARGET_ASM_UNALIGNED_HI_OP +#define TARGET_ASM_UNALIGNED_HI_OP TARGET_ASM_ALIGNED_HI_OP +#undef TARGET_ASM_UNALIGNED_SI_OP +#define TARGET_ASM_UNALIGNED_SI_OP TARGET_ASM_ALIGNED_SI_OP +#undef TARGET_ASM_UNALIGNED_DI_OP +#define TARGET_ASM_UNALIGNED_DI_OP TARGET_ASM_ALIGNED_DI_OP + +/* Target hook implementations. */ + +/* Implements hook TARGET_RETURN_IN_MEMORY. */ +static bool +cr16_return_in_memory (const_tree type, const_tree fntype ATTRIBUTE_UNUSED) +{ + const HOST_WIDE_INT size = int_size_in_bytes (type); + return ((size == -1) || (size > 8)); +} + +/* Implement TARGET_CLASS_LIKELY_SPILLED_P. */ +static bool +cr16_class_likely_spilled_p (reg_class_t rclass) +{ + if ((rclass) == SHORT_REGS || (rclass) == DOUBLE_BASE_REGS + || (rclass) == LONG_REGS || (rclass) == GENERAL_REGS) + return true; + + return false; +} + +static int +cr16_return_pops_args (tree fundecl ATTRIBUTE_UNUSED, + tree funtype ATTRIBUTE_UNUSED, + int size ATTRIBUTE_UNUSED) +{ + return 0; +} + +/* Returns true if data model selected via command line option + is same as function argument. */ +bool +cr16_is_data_model (enum data_model_type model) +{ + return (model == data_model); +} + +/* Parse relevant options and override. */ +static void +cr16_override_options (void) +{ + /* Disable -fdelete-null-pointer-checks option for CR16 target. + Programs which rely on NULL pointer dereferences _not_ halting the + program may not work properly with this option. So disable this + option. */ + flag_delete_null_pointer_checks = 0; + + /* FIXME: To avoid spill_failure ICE during exception handling, + * disable cse_fllow_jumps. The spill error occurs when compiler + * can't find a suitable candidate in GENERAL_REGS class to reload + * a 32bit register. + * Need to find a better way of avoiding this situation. */ + if (flag_exceptions) + flag_cse_follow_jumps = 0; + + /* If -fpic option, data_model == DM_FAR. */ + if (flag_pic == NEAR_PIC) + { + data_model = DM_FAR; + } + + /* The only option we want to examine is data model option. */ + if (cr16_data_model) + { + if (strcmp (cr16_data_model, "medium") == 0) + data_model = DM_DEFAULT; + else if (strcmp (cr16_data_model, "near") == 0) + data_model = DM_NEAR; + else if (strcmp (cr16_data_model, "far") == 0) + { + if (TARGET_CR16CP) + data_model = DM_FAR; + else + error ("data-model=far not valid for cr16c architecture"); + } + else + error ("invalid data model option -mdata-model=%s", cr16_data_model); + } + else + data_model = DM_DEFAULT; +} + +/* Implements the macro TARGET_CONDITIONAL_REGISTER_USAGE. */ +static void +cr16_conditional_register_usage (void) +{ + if (flag_pic) + { + fixed_regs[12] = call_used_regs[12] = 1; + } +} + +/* Stack layout and calling conventions routines. */ + +/* Return nonzero if the current function being compiled is an interrupt + function as specified by the "interrupt" attribute. */ +int +cr16_interrupt_function_p (void) +{ + tree attributes; + + attributes = TYPE_ATTRIBUTES (TREE_TYPE (current_function_decl)); + return (lookup_attribute ("interrupt", attributes) != NULL_TREE); +} + +/* Compute values for the array current_frame_info.save_regs and the variable + current_frame_info.reg_size. The index of current_frame_info.save_regs + is numbers of register, each will get 1 if we need to save it in the + current function, 0 if not. current_frame_info.reg_size is the total sum + of the registers being saved. */ +static void +cr16_compute_save_regs (void) +{ + unsigned int regno; + + /* Initialize here so in case the function is no-return it will be -1. */ + current_frame_info.last_reg_to_save = -1; + + /* Initialize the number of bytes to be saved. */ + current_frame_info.reg_size = 0; + + /* No need to save any registers if the function never returns. */ + if (FUNC_IS_NORETURN_P (current_function_decl) && !MUST_SAVE_REGS_P ()) + return; + + for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) + { + if (fixed_regs[regno]) + { + current_frame_info.save_regs[regno] = 0; + continue; + } + + /* If this reg is used and not call-used (except RA), save it. */ + if (cr16_interrupt_function_p ()) + { + if (!current_function_is_leaf && call_used_regs[regno]) + /* This is a volatile reg in a non-leaf interrupt routine - save + it for the sake of its sons. */ + current_frame_info.save_regs[regno] = 1; + else if (df_regs_ever_live_p (regno)) + /* This reg is used - save it. */ + current_frame_info.save_regs[regno] = 1; + else + /* This reg is not used, and is not a volatile - don't save. */ + current_frame_info.save_regs[regno] = 0; + } + else + { + /* If this reg is used and not call-used (except RA), save it. */ + if (df_regs_ever_live_p (regno) + && (!call_used_regs[regno] || regno == RETURN_ADDRESS_REGNUM)) + current_frame_info.save_regs[regno] = 1; + else + current_frame_info.save_regs[regno] = 0; + } + } + + /* Save registers so the exception handler can modify them. */ + if (crtl->calls_eh_return) + { + unsigned int i; + + for (i = 0;; ++i) + { + regno = EH_RETURN_DATA_REGNO (i); + if (INVALID_REGNUM == regno) + break; + current_frame_info.save_regs[regno] = 1; + } + } + + for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) + if (current_frame_info.save_regs[regno] == 1) + { + current_frame_info.last_reg_to_save = regno; + if (regno >= CR16_FIRST_DWORD_REGISTER) + current_frame_info.reg_size += CR16_UNITS_PER_DWORD; + else + current_frame_info.reg_size += UNITS_PER_WORD; + } +} + +/* Compute the size of the local area and the size to be adjusted by the + prologue and epilogue. */ +static void +cr16_compute_frame (void) +{ + /* For aligning the local variables. */ + int stack_alignment = STACK_BOUNDARY / BITS_PER_UNIT; + int padding_locals; + + /* Padding needed for each element of the frame. */ + current_frame_info.var_size = get_frame_size (); + + /* Align to the stack alignment. */ + padding_locals = current_frame_info.var_size % stack_alignment; + if (padding_locals) + padding_locals = stack_alignment - padding_locals; + + current_frame_info.var_size += padding_locals; + current_frame_info.total_size = current_frame_info.var_size + + (ACCUMULATE_OUTGOING_ARGS + ? crtl->outgoing_args_size : 0); +} + +/* Implements the macro INITIAL_ELIMINATION_OFFSET, return the OFFSET. */ +int +cr16_initial_elimination_offset (int from, int to) +{ + /* Compute this since we need to use current_frame_info.reg_size. */ + cr16_compute_save_regs (); + + /* Compute this since we need to use current_frame_info.var_size. */ + cr16_compute_frame (); + + if (((from) == FRAME_POINTER_REGNUM) && ((to) == STACK_POINTER_REGNUM)) + return (ACCUMULATE_OUTGOING_ARGS ? crtl->outgoing_args_size : 0); + else if (((from) == ARG_POINTER_REGNUM) && ((to) == FRAME_POINTER_REGNUM)) + return (current_frame_info.reg_size + current_frame_info.var_size); + else if (((from) == ARG_POINTER_REGNUM) && ((to) == STACK_POINTER_REGNUM)) + return (current_frame_info.reg_size + current_frame_info.var_size + + (ACCUMULATE_OUTGOING_ARGS ? crtl->outgoing_args_size : 0)); + else + gcc_unreachable (); +} + +/* Register Usage. */ + +/* Return the class number of the smallest class containing reg number REGNO. + This could be a conditional expression or could index an array. */ +enum reg_class +cr16_regno_reg_class (int regno) +{ + if ((regno >= 0) && (regno < CR16_FIRST_DWORD_REGISTER)) + return SHORT_REGS; + + if ((regno >= CR16_FIRST_DWORD_REGISTER) && (regno < FIRST_PSEUDO_REGISTER)) + return LONG_REGS; + + return NO_REGS; +} + +/* Return 1 if hard register REGNO can hold a value of machine-mode MODE. */ +int +cr16_hard_regno_mode_ok (int regno, enum machine_mode mode) +{ + if ((GET_MODE_SIZE (mode) >= 4) && (regno == 11)) + return 0; + + if (mode == DImode || mode == DFmode) + { + if ((regno > 8) || (regno & 1)) + return 0; + return 1; + } + + if ((TARGET_INT32) + && ((regno >= 12) && (GET_MODE_SIZE (mode) < 4 ))) + return 0; + + /* CC can only hold CCmode values. */ + if (GET_MODE_CLASS (mode) == MODE_CC) + return 0; + return 1; +} + +/* Returns register number for function return value.*/ +static inline unsigned int +cr16_ret_register (void) +{ + return 0; +} + +/* Implements hook TARGET_STRUCT_VALUE_RTX. */ +static rtx +cr16_struct_value_rtx (tree fntype ATTRIBUTE_UNUSED, + int incoming ATTRIBUTE_UNUSED) +{ + return gen_rtx_REG (Pmode, cr16_ret_register ()); +} + +/* Returning function value. */ + +/* Worker function for TARGET_FUNCTION_VALUE_REGNO_P. */ +static bool +cr16_function_value_regno_p (const unsigned int regno) +{ + return (regno == cr16_ret_register ()); +} + +/* Create an RTX representing the place where a + library function returns a value of mode MODE. */ +static rtx +cr16_libcall_value (enum machine_mode mode, + const_rtx func ATTRIBUTE_UNUSED) +{ + return gen_rtx_REG (mode, cr16_ret_register ()); +} + +/* Create an RTX representing the place where a + function returns a value of data type VALTYPE. */ +static rtx +cr16_function_value (const_tree type, + const_tree fn_decl_or_type ATTRIBUTE_UNUSED, + bool outgoing ATTRIBUTE_UNUSED) +{ + return gen_rtx_REG (TYPE_MODE (type), cr16_ret_register ()); +} + +/* Passing function arguments. */ + +/* If enough param regs are available for passing the param of type TYPE return + the number of registers needed else 0. */ +static int +enough_regs_for_param (CUMULATIVE_ARGS * cum, const_tree type, + enum machine_mode mode) +{ + int type_size; + int remaining_size; + + if (mode != BLKmode) + type_size = GET_MODE_BITSIZE (mode); + else + type_size = int_size_in_bytes (type) * BITS_PER_UNIT; + + remaining_size = BITS_PER_WORD * (MAX_REG_FOR_PASSING_ARGS + - (MIN_REG_FOR_PASSING_ARGS + cum->ints) + + 1); + + /* Any variable which is too big to pass in two registers, will pass on + stack. */ + if ((remaining_size >= type_size) && (type_size <= 2 * BITS_PER_WORD)) + return (type_size + BITS_PER_WORD - 1) / BITS_PER_WORD; + + return 0; +} + +/* Implements the macro FUNCTION_ARG defined in cr16.h. */ +static rtx +cr16_function_arg (cumulative_args_t cum_v, enum machine_mode mode, + const_tree type, bool named ATTRIBUTE_UNUSED) +{ + CUMULATIVE_ARGS *cum = get_cumulative_args (cum_v); + cum->last_parm_in_reg = 0; + + /* function_arg () is called with this type just after all the args have + had their registers assigned. The rtx that function_arg returns from + this type is supposed to pass to 'gen_call' but currently it is not + implemented (see macro GEN_CALL). */ + if (type == void_type_node) + return NULL_RTX; + + if (targetm.calls.must_pass_in_stack (mode, type) || (cum->ints < 0)) + return NULL_RTX; + + if (mode == BLKmode) + { + /* Enable structures that need padding bytes at the end to pass to a + function in registers. */ + if (enough_regs_for_param (cum, type, mode) != 0) + { + cum->last_parm_in_reg = 1; + return gen_rtx_REG (mode, MIN_REG_FOR_PASSING_ARGS + cum->ints); + } + } + + if ((MIN_REG_FOR_PASSING_ARGS + cum->ints) > MAX_REG_FOR_PASSING_ARGS) + return NULL_RTX; + else + { + if (enough_regs_for_param (cum, type, mode) != 0) + { + cum->last_parm_in_reg = 1; + return gen_rtx_REG (mode, MIN_REG_FOR_PASSING_ARGS + cum->ints); + } + } + + return NULL_RTX; +} + +/* Implements the macro INIT_CUMULATIVE_ARGS defined in cr16.h. */ +void +cr16_init_cumulative_args (CUMULATIVE_ARGS * cum, tree fntype, + rtx libfunc ATTRIBUTE_UNUSED) +{ + tree param, next_param; + + cum->ints = 0; + + /* Determine if this function has variable arguments. This is indicated by + the last argument being 'void_type_mode' if there are no variable + arguments. Change here for a different vararg. */ + for (param = (fntype) ? TYPE_ARG_TYPES (fntype) : 0; + param != NULL_TREE; param = next_param) + { + next_param = TREE_CHAIN (param); + if ((next_param == NULL_TREE) && (TREE_VALUE (param) != void_type_node)) + { + cum->ints = -1; + return; + } + } +} + +/* Implements the macro FUNCTION_ARG_ADVANCE defined in cr16.h. */ +static void +cr16_function_arg_advance (cumulative_args_t cum_v, enum machine_mode mode, + const_tree type, bool named ATTRIBUTE_UNUSED) +{ + CUMULATIVE_ARGS * cum = get_cumulative_args (cum_v); + + /* l holds the number of registers required. */ + int l = GET_MODE_BITSIZE (mode) / BITS_PER_WORD; + + /* If the parameter isn't passed on a register don't advance cum. */ + if (!cum->last_parm_in_reg) + return; + + if (targetm.calls.must_pass_in_stack (mode, type) || (cum->ints < 0)) + return; + + if ((mode == SImode) || (mode == HImode) + || (mode == QImode) || (mode == DImode)) + { + if (l <= 1) + cum->ints += 1; + else + cum->ints += l; + } + else if ((mode == SFmode) || (mode == DFmode)) + cum->ints += l; + else if ((mode) == BLKmode) + { + if ((l = enough_regs_for_param (cum, type, mode)) != 0) + cum->ints += l; + } + return; +} + +/* Implements the macro FUNCTION_ARG_REGNO_P defined in cr16.h. + Return nonzero if N is a register used for passing parameters. */ +int +cr16_function_arg_regno_p (int n) +{ + return ((n <= MAX_REG_FOR_PASSING_ARGS) && (n >= MIN_REG_FOR_PASSING_ARGS)); +} + +/* Addressing modes. + Following set of function implement the macro GO_IF_LEGITIMATE_ADDRESS + defined in cr16.h. */ + +/* Helper function to check if is a valid base register that can + hold address. */ +static int +cr16_addr_reg_p (rtx addr_reg) +{ + rtx reg; + + if (REG_P (addr_reg)) + reg = addr_reg; + else if ((GET_CODE (addr_reg) == SUBREG) + && REG_P (SUBREG_REG (addr_reg)) + && (GET_MODE_SIZE (GET_MODE (SUBREG_REG (addr_reg))) + <= UNITS_PER_WORD)) + reg = SUBREG_REG (addr_reg); + else + return FALSE; + + if (GET_MODE (reg) != Pmode) + return FALSE; + + return TRUE; +} + +/* Helper functions: Created specifically for decomposing operand of CONST + Recursively look into expression x for code or data symbol. + The function expects the expression to contain combination of + SYMBOL_REF, CONST_INT, (PLUS or MINUS) + LABEL_REF, CONST_INT, (PLUS or MINUS) + SYMBOL_REF + LABEL_REF + All other combinations will result in code = -1 and data = ILLEGAL_DM + code data + -1 ILLEGAL_DM The expression did not contain SYMBOL_REF or LABEL_REF + 0 DM_FAR SYMBOL_REF was found and it was far data reference. + 0 DM_DEFAULT SYMBOL_REF was found and it was medium data reference. + 1 ILLEGAL_DM LABEL_REF was found. + 2 ILLEGAL_DM SYMBOL_REF was found and it was function reference. */ +void +cr16_decompose_const (rtx x, int *code, enum data_model_type *data, + bool treat_as_const) +{ + *code = -1; + *data = ILLEGAL_DM; + switch (GET_CODE (x)) + { + case SYMBOL_REF: + *code = SYMBOL_REF_FUNCTION_P (x) ? 2 : 0; + /* 2 indicates func sym. */ + if (*code == 0) + { + if (CR16_TARGET_DATA_NEAR) + *data = DM_DEFAULT; + else if (CR16_TARGET_DATA_MEDIUM) + *data = DM_FAR; + else if (CR16_TARGET_DATA_FAR) + { + if (treat_as_const) + /* This will be used only for printing + the qualifier. This call is (may be) + made by cr16_print_operand_address. */ + *data = DM_FAR; + else + /* This call is (may be) made by + cr16_legitimate_address_p. */ + *data = ILLEGAL_DM; + } + } + return; + + case LABEL_REF: + /* 1 - indicates non-function symbol. */ + *code = 1; + return; + + case PLUS: + case MINUS: + /* Look into the tree nodes. */ + if (GET_CODE (XEXP (x, 0)) == CONST_INT) + cr16_decompose_const (XEXP (x, 1), code, data, treat_as_const); + else if (GET_CODE (XEXP (x, 1)) == CONST_INT) + cr16_decompose_const (XEXP (x, 0), code, data, treat_as_const); + return; + default: + return; + } +} + +/* Decompose Address + This function decomposes the address returns the type of address + as defined in enum cr16_addrtype. It also fills the parameter *out. + The decomposed address can be used for two purposes. One to + check if the address is valid and second to print the address + operand. + + Following tables list valid address supported in CR16C/C+ architectures. + Legend: + aN : Absoulte address N-bit address + R : One 16-bit register + RP : Consecutive two 16-bit registers or one 32-bit register + I : One 32-bit register + dispN : Signed displacement of N-bits + + ----Code addresses---- + Branch operands: + disp9 : CR16_ABSOLUTE (disp) + disp17 : CR16_ABSOLUTE (disp) + disp25 : CR16_ABSOLUTE (disp) + RP + disp25 : CR16_REGP_REL (base, disp) + + Jump operands: + RP : CR16_REGP_REL (base, disp=0) + a24 : CR16_ABSOLUTE (disp) + + ----Data addresses---- + a20 : CR16_ABSOLUTE (disp) near (1M) + a24 : CR16_ABSOLUTE (disp) medium (16M) + R + d20 : CR16_REG_REL (base, disp) near (1M+64K) + RP + d4 : CR16_REGP_REL (base, disp) far (4G) + RP + d16 : CR16_REGP_REL (base, disp) far (4G) + RP + d20 : CR16_REGP_REL (base, disp) far (4G) + I : *** Valid but port does not support this + I + a20 : *** Valid but port does not support this + I + RP + d14: CR16_INDEX_REGP_REL (base, index, disp) far (4G) + I + RP + d20: CR16_INDEX_REGP_REL (base, index, disp) far (4G) + + Decomposing Data model in case of absolute address. + + Target Option Address type Resultant Data ref type + ---------------------- ------------ ----------------------- + CR16_TARGET_MODEL_NEAR ABS20 DM_DEFAULT + CR16_TARGET_MODEL_NEAR IMM20 DM_DEFAULT + CR16_TARGET_MODEL_NEAR ABS24 Invalid + CR16_TARGET_MODEL_NEAR IMM32 Invalid + + CR16_TARGET_MODEL_MEDIUM ABS20 DM_DEFAULT + CR16_TARGET_MODEL_MEDIUM IMM20 DM_DEFAULT + CR16_TARGET_MODEL_MEDIUM ABS24 DM_FAR + CR16_TARGET_MODEL_MEDIUM IMM32 Invalid + + CR16_TARGET_MODEL_FAR ABS20 DM_DEFAULT + CR16_TARGET_MODEL_FAR IMM20 DM_DEFAULT + CR16_TARGET_MODEL_FAR ABS24 DM_FAR + CR16_TARGET_MODEL_FAR IMM32 DM_FAR. */ +enum cr16_addrtype +cr16_decompose_address (rtx addr, struct cr16_address *out, + bool debug_print, bool treat_as_const) +{ + rtx base = NULL_RTX, disp = NULL_RTX, index = NULL_RTX; + enum data_model_type data = ILLEGAL_DM; + int code = -1; + enum cr16_addrtype retval = CR16_INVALID; + + switch (GET_CODE (addr)) + { + case CONST_INT: + /* Absolute address (known at compile time). */ + code = 0; + if (debug_print) + fprintf (stderr, "\ncode:%d", code); + disp = addr; + + if (debug_print) + { + fprintf (stderr, "\ndisp:"); + debug_rtx (disp); + } + + if (UNSIGNED_INT_FITS_N_BITS (INTVAL (disp), 20)) + { + data = DM_DEFAULT; + if (debug_print) + fprintf (stderr, "\ndata:%d", data); + retval = CR16_ABSOLUTE; + } + else if (UNSIGNED_INT_FITS_N_BITS (INTVAL (disp), 24)) + { + if (!CR16_TARGET_DATA_NEAR) + { + data = DM_FAR; + if (debug_print) + fprintf (stderr, "\ndata:%d", data); + retval = CR16_ABSOLUTE; + } + else + return CR16_INVALID; /* ABS24 is not support in NEAR model. */ + } + else + return CR16_INVALID; + break; + + case CONST: + /* A CONST is an expression of PLUS or MINUS with + CONST_INT, SYMBOL_REF or LABEL_REF. This is the + result of assembly-time arithmetic computation. */ + retval = CR16_ABSOLUTE; + disp = addr; + /* Call the helper function to check the validity. */ + cr16_decompose_const (XEXP (addr, 0), &code, &data, treat_as_const); + if ((code == 0) && (data == ILLEGAL_DM)) + /* CONST is not valid code or data address. */ + return CR16_INVALID; + if (debug_print) + { + fprintf (stderr, "\ndisp:"); + debug_rtx (disp); + fprintf (stderr, "\ncode:%d", code); + fprintf (stderr, "\ndata:%d", data); + } + break; + + case LABEL_REF: + retval = CR16_ABSOLUTE; + disp = addr; + /* 1 - indicates non-function symbol. */ + code = 1; + if (debug_print) + { + fprintf (stderr, "\ndisp:"); + debug_rtx (disp); + fprintf (stderr, "\ncode:%d", code); + } + break; + + case SYMBOL_REF: + /* Absolute address (known at link time). */ + retval = CR16_ABSOLUTE; + disp = addr; + /* This is a code address if symbol_ref is a function. */ + /* 2 indicates func sym. */ + code = SYMBOL_REF_FUNCTION_P (addr) ? 2 : 0; + if (debug_print) + { + fprintf (stderr, "\ndisp:"); + debug_rtx (disp); + fprintf (stderr, "\ncode:%d", code); + } + /* If not function ref then check if valid data ref. */ + if (code == 0) + { + if (CR16_TARGET_DATA_NEAR) + data = DM_DEFAULT; + else if (CR16_TARGET_DATA_MEDIUM) + data = DM_FAR; + else if (CR16_TARGET_DATA_FAR) + { + if (treat_as_const) + /* This will be used only for printing the + qualifier. This call is (may be) made + by cr16_print_operand_address. */ + data = DM_FAR; + else + /* This call is (may be) made by + cr16_legitimate_address_p. */ + return CR16_INVALID; + } + else + data = DM_DEFAULT; + } + if (debug_print) + fprintf (stderr, "\ndata:%d", data); + break; + + case REG: + case SUBREG: + /* Register relative address. */ + /* Assume REG fits in a single register. */ + retval = CR16_REG_REL; + if (GET_MODE_BITSIZE (GET_MODE (addr)) > BITS_PER_WORD) + if (!LONG_REG_P (REGNO (addr))) + /* REG will result in reg pair. */ + retval = CR16_REGP_REL; + base = addr; + if (debug_print) + { + fprintf (stderr, "\nbase:"); + debug_rtx (base); + } + break; + + case PLUS: + switch (GET_CODE (XEXP (addr, 0))) + { + case REG: + case SUBREG: + /* REG + DISP20. */ + /* All Reg relative addresses having a displacement needs + to fit in 20-bits. */ + disp = XEXP (addr, 1); + if (debug_print) + { + fprintf (stderr, "\ndisp:"); + debug_rtx (disp); + } + switch (GET_CODE (XEXP (addr, 1))) + { + case CONST_INT: + /* Shall fit in 20-bits. */ + if (!UNSIGNED_INT_FITS_N_BITS (INTVAL (disp), 20)) + return CR16_INVALID; + code = 0; + if (debug_print) + fprintf (stderr, "\ncode:%d", code); + break; + + case UNSPEC: + switch (XINT (XEXP (addr, 1), 1)) + { + case UNSPEC_LIBRARY_OFFSET: + default: + gcc_unreachable (); + } + break; + + case LABEL_REF: + case SYMBOL_REF: + case CONST: + /* This is also a valid expression for address. + However, we cannot ascertain if the resultant + displacement will be valid 20-bit value. Therefore, + lets not allow such an expression for now. This will + be updated when we find a way to validate this + expression as legitimate address. + Till then fall through CR16_INVALID. */ + default: + return CR16_INVALID; + } + + /* Now check if REG can fit into single or pair regs. */ + retval = CR16_REG_REL; + base = XEXP (addr, 0); + if (debug_print) + { + fprintf (stderr, "\nbase:"); + debug_rtx (base); + } + if (GET_MODE_BITSIZE (GET_MODE ((XEXP (addr, 0)))) > BITS_PER_WORD) + { + if (!LONG_REG_P (REGNO ((XEXP (addr, 0))))) + /* REG will result in reg pair. */ + retval = CR16_REGP_REL; + } + break; + + case PLUS: + /* Valid expr: + plus + /\ + / \ + plus idx + /\ + / \ + reg const_int + + Check if the operand 1 is valid index register. */ + data = ILLEGAL_DM; + if (debug_print) + fprintf (stderr, "\ndata:%d", data); + switch (GET_CODE (XEXP (addr, 1))) + { + case REG: + case SUBREG: + if (!REG_OK_FOR_INDEX_P (XEXP (addr, 1))) + return CR16_INVALID; + /* OK. REG is a valid index register. */ + index = XEXP (addr, 1); + if (debug_print) + { + fprintf (stderr, "\nindex:"); + debug_rtx (index); + } + break; + default: + return CR16_INVALID; + } + /* Check if operand 0 of operand 0 is REGP. */ + switch (GET_CODE (XEXP (XEXP (addr, 0), 0))) + { + case REG: + case SUBREG: + /* Now check if REG is a REGP and not in LONG regs. */ + if (GET_MODE_BITSIZE (GET_MODE (XEXP (XEXP (addr, 0), 0))) + > BITS_PER_WORD) + { + if (REGNO (XEXP (XEXP (addr, 0), 0)) + >= CR16_FIRST_DWORD_REGISTER) + return CR16_INVALID; + base = XEXP (XEXP (addr, 0), 0); + if (debug_print) + { + fprintf (stderr, "\nbase:"); + debug_rtx (base); + } + } + else + return CR16_INVALID; + break; + default: + return CR16_INVALID; + } + /* Now check if the operand 1 of operand 0 is const_int. */ + if (GET_CODE (XEXP (XEXP (addr, 0), 1)) == CONST_INT) + { + disp = XEXP (XEXP (addr, 0), 1); + if (debug_print) + { + fprintf (stderr, "\ndisp:"); + debug_rtx (disp); + } + if (!UNSIGNED_INT_FITS_N_BITS (INTVAL (disp), 20)) + return CR16_INVALID; + } + else + return CR16_INVALID; + retval = CR16_INDEX_REGP_REL; + break; + default: + return CR16_INVALID; + } + break; + + default: + return CR16_INVALID; + } + + /* Check if the base and index registers are valid. */ + if (base && !(cr16_addr_reg_p (base))) + return CR16_INVALID; + if (base && !(CR16_REG_OK_FOR_BASE_P (base))) + return CR16_INVALID; + if (index && !(REG_OK_FOR_INDEX_P (index))) + return CR16_INVALID; + + /* Write the decomposition to out parameter. */ + out->base = base; + out->disp = disp; + out->index = index; + out->data = data; + out->code = code; + + return retval; +} + +/* Return non-zero value if 'x' is legitimate PIC operand + when generating PIC code. */ +int +legitimate_pic_operand_p (rtx x) +{ + switch (GET_CODE (x)) + { + case SYMBOL_REF: + return 0; + break; + case LABEL_REF: + return 0; + break; + case CONST: + /* REVISIT: Use something like symbol_referenced_p. */ + if (GET_CODE (XEXP (x, 0)) == PLUS + && (GET_CODE (XEXP (XEXP (x, 0), 0)) == SYMBOL_REF + || GET_CODE (XEXP (XEXP (x, 0), 0)) == LABEL_REF) + && (GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT)) + return 0; + break; + case MEM: + return legitimate_pic_operand_p (XEXP (x, 0)); + break; + default: + break; + } + return 1; +} + +/* Convert a non-PIC address in `orig' to a PIC address in `reg'. + + Input Output (-f pic) Output (-f PIC) + orig reg + + C1 symbol symbol@BRO (r12) symbol@GOT (r12) + + C2 symbol + offset symbol+offset@BRO (r12) symbol+offset@GOT (r12) + + NOTE: @BRO is added using unspec:BRO + NOTE: @GOT is added using unspec:GOT. */ +rtx +legitimize_pic_address (rtx orig, enum machine_mode mode ATTRIBUTE_UNUSED, + rtx reg) +{ + /* First handle a simple SYMBOL_REF or LABEL_REF. */ + if (GET_CODE (orig) == SYMBOL_REF || GET_CODE (orig) == LABEL_REF) + { + if (reg == 0) + reg = gen_reg_rtx (Pmode); + + if (flag_pic == NEAR_PIC) + { + /* Unspec to handle -fpic option. */ + emit_insn (gen_unspec_bro_addr (reg, orig)); + emit_insn (gen_addsi3 (reg, reg, pic_offset_table_rtx)); + } + else if (flag_pic == FAR_PIC) + { + /* Unspec to handle -fPIC option. */ + emit_insn (gen_unspec_got_addr (reg, orig)); + } + return reg; + } + else if (GET_CODE (orig) == CONST) + { + /* To handle (symbol + offset). */ + rtx base, offset; + + if (GET_CODE (XEXP (orig, 0)) == PLUS + && XEXP (XEXP (orig, 0), 0) == pic_offset_table_rtx) + return orig; + + if (reg == 0) + { + gcc_assert (can_create_pseudo_p ()); + reg = gen_reg_rtx (Pmode); + } + + gcc_assert (GET_CODE (XEXP (orig, 0)) == PLUS); + + base = legitimize_pic_address (XEXP (XEXP (orig, 0), 0), Pmode, reg); + offset = legitimize_pic_address (XEXP (XEXP (orig, 0), 1), Pmode, + base == reg ? 0 : reg); + + /* REVISIT: Optimize for const-offsets. */ + emit_insn (gen_addsi3 (reg, base, offset)); + + return reg; + } + return orig; +} + +/* Implementation of TARGET_LEGITIMATE_ADDRESS_P. */ +static bool +cr16_legitimate_address_p (enum machine_mode mode ATTRIBUTE_UNUSED, + rtx addr, bool strict) +{ + enum cr16_addrtype addrtype; + struct cr16_address address; + + if (TARGET_DEBUG_ADDR) + { + fprintf (stderr, + "\n======\nTARGET_LEGITIMATE_ADDRESS_P, mode = %s, strict = %d", + GET_MODE_NAME (mode), strict); + debug_rtx (addr); + } + addrtype = cr16_decompose_address (addr, &address, + (TARGET_DEBUG_ADDR ? 1 : 0), FALSE); + + if (TARGET_DEBUG_ADDR) + { + const char *typestr; + + switch (addrtype) + { + case CR16_INVALID: + typestr = "invalid"; + break; + case CR16_ABSOLUTE: + typestr = "absolute"; + break; + case CR16_REG_REL: + typestr = "register relative"; + break; + case CR16_REGP_REL: + typestr = "register pair relative"; + break; + case CR16_INDEX_REGP_REL: + typestr = "index + register pair relative"; + break; + default: + gcc_unreachable (); + } + fprintf (stderr, "\ncr16 address type: %s\n", typestr); + } + + if (addrtype == CR16_INVALID) + return FALSE; + + if (strict) + { + if (address.base + && !REGNO_MODE_OK_FOR_BASE_P (REGNO (address.base), mode)) + { + if (TARGET_DEBUG_ADDR) + fprintf (stderr, "base register not strict\n"); + return FALSE; + } + if (address.index && !REGNO_OK_FOR_INDEX_P (REGNO (address.index))) + { + if (TARGET_DEBUG_ADDR) + fprintf (stderr, "index register not strict\n"); + return FALSE; + } + } + + /* Return true if addressing mode is register relative. */ + if (flag_pic) + { + if (addrtype == CR16_REG_REL || addrtype == CR16_REGP_REL) + return TRUE; + else + return FALSE; + } + + return TRUE; +} + +/* Routines to compute costs. */ + +/* Return cost of the memory address x. */ +static int +cr16_address_cost (rtx addr, bool speed ATTRIBUTE_UNUSED) +{ + enum cr16_addrtype addrtype; + struct cr16_address address; + int cost = 2; + + addrtype = cr16_decompose_address (addr, &address, 0, FALSE); + + gcc_assert (addrtype != CR16_INVALID); + + /* CR16_ABSOLUTE : 3 + CR16_REG_REL (disp !=0) : 4 + CR16_REG_REL (disp ==0) : 5 + CR16_REGP_REL (disp !=0) : 6 + CR16_REGP_REL (disp ==0) : 7 + CR16_INDEX_REGP_REL (disp !=0) : 8 + CR16_INDEX_REGP_REL (disp ==0) : 9. */ + switch (addrtype) + { + case CR16_ABSOLUTE: + cost += 1; + break; + case CR16_REGP_REL: + cost += 2; + /* Fall through. */ + case CR16_REG_REL: + cost += 3; + if (address.disp) + cost -= 1; + break; + case CR16_INDEX_REGP_REL: + cost += 7; + if (address.disp) + cost -= 1; + default: + break; + } + + if (TARGET_DEBUG_ADDR) + { + fprintf (stderr, "\n======\nmacro TARGET_ADDRESS_COST = %d\n", cost); + debug_rtx (addr); + } + + return cost; +} + + +/* Implement `TARGET_REGISTER_MOVE_COST'. */ +static int +cr16_register_move_cost (enum machine_mode mode ATTRIBUTE_UNUSED, + reg_class_t from ATTRIBUTE_UNUSED, reg_class_t to) +{ + return (to != GENERAL_REGS ? 8 : 2); +} + +/* Implement `TARGET_MEMORY_MOVE_COST'. */ + +/* Return the cost of moving data of mode MODE between a register of class + CLASS and memory; IN is zero if the value is to be written to memory, + nonzero if it is to be read in. This cost is relative to those in + REGISTER_MOVE_COST. */ +static int +cr16_memory_move_cost (enum machine_mode mode, + reg_class_t rclass ATTRIBUTE_UNUSED, + bool in ATTRIBUTE_UNUSED) +{ + /* One LD or ST takes twice the time of a simple reg-reg move. */ + if (reg_classes_intersect_p (rclass, GENERAL_REGS)) + return (4 * HARD_REGNO_NREGS (0, mode)); + else + return (100); +} + +/* Instruction output. */ + +/* Check if a const_double is ok for cr16 store-immediate instructions. */ +int +cr16_const_double_ok (rtx op) +{ + if (GET_MODE (op) == SFmode) + { + REAL_VALUE_TYPE r; + long l; + REAL_VALUE_FROM_CONST_DOUBLE (r, op); + REAL_VALUE_TO_TARGET_SINGLE (r, l); + return UNSIGNED_INT_FITS_N_BITS (l, 4) ? 1 : 0; + } + + return ((UNSIGNED_INT_FITS_N_BITS (CONST_DOUBLE_LOW (op), 4)) && + (UNSIGNED_INT_FITS_N_BITS (CONST_DOUBLE_HIGH (op), 4))) ? 1 : 0; +} + +/* Returns bit position of first 0 or 1 bit. + It is safe to assume val as 16-bit wide. */ +int +cr16_operand_bit_pos (int val, int bitval) +{ + int i; + if (bitval == 0) + val = ~val; + + for (i = 0; i < 16; i++) + if (val & (1 << i)) + break; + return i; +} + +/* Implements the macro PRINT_OPERAND defined in cr16.h. */ +static void +cr16_print_operand (FILE * file, rtx x, int code) +{ + int ptr_dereference = 0; + + switch (code) + { + case 'd': + { + const char *cr16_cmp_str; + switch (GET_CODE (x)) + { + /* MD: compare (reg, reg or imm) but CR16: cmp (reg or imm, reg) + -> swap all non symmetric ops. */ + case EQ: + cr16_cmp_str = "eq"; + break; + case NE: + cr16_cmp_str = "ne"; + break; + case GT: + cr16_cmp_str = "lt"; + break; + case GTU: + cr16_cmp_str = "lo"; + break; + case LT: + cr16_cmp_str = "gt"; + break; + case LTU: + cr16_cmp_str = "hi"; + break; + case GE: + cr16_cmp_str = "le"; + break; + case GEU: + cr16_cmp_str = "ls"; + break; + case LE: + cr16_cmp_str = "ge"; + break; + case LEU: + cr16_cmp_str = "hs"; + break; + default: + gcc_unreachable (); + } + fprintf (file, "%s", cr16_cmp_str); + return; + } + case '$': + putc ('$', file); + return; + + case 'p': + if (GET_CODE (x) == REG) + { + /* For Push instructions, we should not print register pairs. */ + fprintf (file, "%s", reg_names[REGNO (x)]); + return; + } + break; + + case 'b': + /* Print the immediate address for bal + 'b' is used instead of 'a' to avoid compiler calling + the GO_IF_LEGITIMATE_ADDRESS which cannot + perform checks on const_int code addresses as it + assumes all const_int are data addresses. */ + fprintf (file, "0x%lx", INTVAL (x)); + return; + + case 'r': + /* Print bit position of first 0. */ + fprintf (file, "%d", cr16_operand_bit_pos (INTVAL (x), 0)); + return; + + case 's': + /* Print bit position of first 1. */ + fprintf (file, "%d", cr16_operand_bit_pos (INTVAL (x), 1)); + return; + case 'g': + /* 'g' is used for implicit mem: dereference. */ + ptr_dereference = 1; + case 'f': + case 0: + /* default. */ + switch (GET_CODE (x)) + { + case REG: + if (GET_MODE_BITSIZE (GET_MODE (x)) > BITS_PER_WORD) + { + if (LONG_REG_P (REGNO (x))) + fprintf (file, "(%s)", reg_names[REGNO (x)]); + else + fprintf (file, "(%s,%s)", reg_names[REGNO (x) + 1], + reg_names[REGNO (x)]); + } + else + fprintf (file, "%s", reg_names[REGNO (x)]); + return; + + case MEM: + output_address (XEXP (x, 0)); + return; + + case CONST_DOUBLE: + { + REAL_VALUE_TYPE r; + long l; + + REAL_VALUE_FROM_CONST_DOUBLE (r, x); + REAL_VALUE_TO_TARGET_SINGLE (r, l); + + fprintf (file, "$0x%lx", l); + return; + } + case CONST_INT: + { + fprintf (file, "$%ld", INTVAL (x)); + return; + } + case UNSPEC: + switch (XINT (x, 1)) + { + default: + gcc_unreachable (); + } + break; + + default: + if (!ptr_dereference) + { + putc ('$', file); + } + cr16_print_operand_address (file, x); + return; + } + default: + output_operand_lossage ("invalid %%xn code"); + } + + gcc_unreachable (); +} + +/* Implements the macro PRINT_OPERAND_ADDRESS defined in cr16.h. */ + +static void +cr16_print_operand_address (FILE * file, rtx addr) +{ + enum cr16_addrtype addrtype; + struct cr16_address address; + + /* Decompose the address. Also ask it to treat address as constant. */ + addrtype = cr16_decompose_address (addr, &address, 0, TRUE); + + if (address.disp && GET_CODE (address.disp) == UNSPEC) + { + debug_rtx (addr); + } + + switch (addrtype) + { + case CR16_REG_REL: + if (address.disp) + { + if (GET_CODE (address.disp) == UNSPEC) + cr16_print_operand (file, address.disp, 0); + else + output_addr_const (file, address.disp); + } + else + fprintf (file, "0"); + fprintf (file, "(%s)", reg_names[REGNO (address.base)]); + break; + + case CR16_ABSOLUTE: + if (address.disp) + output_addr_const (file, address.disp); + else + fprintf (file, "0"); + break; + + case CR16_INDEX_REGP_REL: + fprintf (file, "[%s]", reg_names[REGNO (address.index)]); + /* Fall through. */ + case CR16_REGP_REL: + if (address.disp) + { + if (GET_CODE (address.disp) == UNSPEC) + cr16_print_operand (file, address.disp, 0); + else + output_addr_const (file, address.disp); + } + else + fprintf (file, "0"); + fprintf (file, "(%s,%s)", reg_names[REGNO (address.base) + 1], + reg_names[REGNO (address.base)]); + break; + default: + debug_rtx (addr); + gcc_unreachable (); + } + /* Add qualifiers to the address expression that was just printed. */ + if (flag_pic < NEAR_PIC && address.code == 0) + { + if (address.data == DM_FAR) + /* Addr contains SYMBOL_REF & far data ptr. */ + fprintf (file, "@l"); + else if (address.data == DM_DEFAULT) + /* Addr contains SYMBOL_REF & medium data ptr. */ + fprintf (file, "@m"); + /* Addr contains SYMBOL_REF & medium data ptr. */ + else if (address.data == DM_NEAR) + /* Addr contains SYMBOL_REF & near data ptr. */ + fprintf (file, "@s"); + } + else if (flag_pic == NEAR_PIC + && (address.code == 0) && (address.data == DM_FAR + || address.data == DM_DEFAULT + || address.data == DM_NEAR)) + { + fprintf (file, "@l"); + } + else if (flag_pic == NEAR_PIC && address.code == 2) + { + fprintf (file, "pic"); + } + else if (flag_pic == NEAR_PIC && address.code == 1) + { + fprintf (file, "@cpic"); + } + + else if (flag_pic == FAR_PIC && address.code == 2) + { + /* REVISIT: cr16 register indirect jump expects a 1-bit right shifted + address ! GOTc tells assembler this symbol is a text-address + This needs to be fixed in such a way that this offset is done + only in the case where an address is being used for indirect jump + or call. Determining the potential usage of loadd is of course not + possible always. Eventually, this has to be fixed in the + processor. */ + fprintf (file, "GOT (%s)", reg_names[PIC_OFFSET_TABLE_REGNUM]); + } + else if (flag_pic == FAR_PIC && address.code == 1) + { + fprintf (file, "@cGOT (%s)", reg_names[PIC_OFFSET_TABLE_REGNUM]); + } + + else if (flag_pic == FAR_PIC && + (address.data == DM_FAR || address.data == DM_DEFAULT + || address.data == DM_NEAR)) + { + fprintf (file, "@GOT (%s)", reg_names[PIC_OFFSET_TABLE_REGNUM]); + } +} + +/* Machine description helper functions. */ + +/* Called from cr16.md. The return value depends on the parameter push_or_pop: + When push_or_pop is zero -> string for push instructions of prologue. + When push_or_pop is nonzero -> string for pop/popret/retx in epilogue. + Relies on the assumptions: + 1. RA is the last register to be saved. + 2. The maximal value of the counter is MAX_COUNT. */ +char * +cr16_prepare_push_pop_string (int push_or_pop) +{ + /* j is the number of registers being saved, takes care that there won't be + more than 8 in one push/pop instruction. */ + + /* For the register mask string. */ + static char one_inst_str[50]; + + /* i is the index of current_frame_info.save_regs[], going from 0 until + current_frame_info.last_reg_to_save. */ + int i, start_reg; + int word_cnt; + int print_ra; + char *return_str; + + /* For reversing on the push instructions if there are more than one. */ + char *temp_str; + + return_str = (char *) xmalloc (160); + temp_str = (char *) xmalloc (160); + + /* Initialize. */ + memset (return_str, 0, 3); + + i = 0; + while (i <= current_frame_info.last_reg_to_save) + { + /* Prepare mask for one instruction. */ + one_inst_str[0] = 0; + + /* To count number of words in one instruction. */ + word_cnt = 0; + start_reg = i; + print_ra = 0; + while ((word_cnt < MAX_COUNT) + && (i <= current_frame_info.last_reg_to_save)) + { + /* For each non consecutive save register, + a new instruction shall be generated. */ + if (!current_frame_info.save_regs[i]) + { + /* Move to next reg and break. */ + ++i; + break; + } + + if (i == RETURN_ADDRESS_REGNUM) + print_ra = 1; + else + { + /* Check especially if adding 2 does not cross the MAX_COUNT. */ + if ((word_cnt + ((i < CR16_FIRST_DWORD_REGISTER) ? 1 : 2)) + >= MAX_COUNT) + break; + /* Increase word count by 2 for long registers except RA. */ + word_cnt += ((i < CR16_FIRST_DWORD_REGISTER) ? 1 : 2); + } + ++i; + } + + /* No need to generate any instruction as + no register or RA needs to be saved. */ + if ((word_cnt == 0) && (print_ra == 0)) + continue; + + /* Now prepare the instruction operands. */ + if (word_cnt > 0) + { + sprintf (one_inst_str, "$%d, %s", word_cnt, reg_names[start_reg]); + if (print_ra) + strcat (one_inst_str, ", ra"); + } + else + strcat (one_inst_str, "ra"); + + if (push_or_pop == 1) + { + /* Pop instruction. */ + if (print_ra && !cr16_interrupt_function_p () + && !crtl->calls_eh_return) + /* Print popret if RA is saved and its not a interrupt + function. */ + strcpy (temp_str, "\n\tpopret\t"); + else + strcpy (temp_str, "\n\tpop\t"); + + strcat (temp_str, one_inst_str); + + /* Add the pop instruction list. */ + strcat (return_str, temp_str); + } + else + { + /* Push instruction. */ + strcpy (temp_str, "\n\tpush\t"); + strcat (temp_str, one_inst_str); + + /* We need to reverse the order of the instructions if there + are more than one. (since the pop will not be reversed in + the epilogue. */ + strcat (temp_str, return_str); + strcpy (return_str, temp_str); + } + } + + if (push_or_pop == 1) + { + /* POP. */ + if (cr16_interrupt_function_p ()) + strcat (return_str, "\n\tretx\n"); + else if (crtl->calls_eh_return) + { + /* Add stack adjustment before returning to exception handler + NOTE: EH_RETURN_STACKADJ_RTX must refer to (r5, r4). */ + strcat (return_str, "\n\taddd\t (r5, r4), (sp)\t\n"); + strcat (return_str, "\n\tjump\t (ra)\n"); + + /* But before anything else, undo the adjustment addition done in + cr16_expand_epilogue (). */ + strcpy (temp_str, "\n\tsubd\t (r5, r4), (sp)\t\n"); + strcat (temp_str, return_str); + strcpy (return_str, temp_str); + } + else if (!FUNC_IS_NORETURN_P (current_function_decl) + && !(current_frame_info.save_regs[RETURN_ADDRESS_REGNUM])) + strcat (return_str, "\n\tjump\t (ra)\n"); + } + + /* Skip the newline and the tab in the start of return_str. */ + return_str += 2; + return return_str; +} + + +/* Generate DWARF2 annotation for multi-push instruction. */ +static void +cr16_create_dwarf_for_multi_push (rtx insn) +{ + rtx dwarf, reg, tmp; + int i, j, from, to, word_cnt, dwarf_par_index, inc; + enum machine_mode mode; + int num_regs = 0, offset = 0, split_here = 0, total_push_bytes = 0; + + for (i = 0; i <= current_frame_info.last_reg_to_save; ++i) + { + if (current_frame_info.save_regs[i]) + { + ++num_regs; + if (i < CR16_FIRST_DWORD_REGISTER) + total_push_bytes += 2; + else + total_push_bytes += 4; + } + } + + if (!num_regs) + return; + + dwarf = gen_rtx_SEQUENCE (VOIDmode, rtvec_alloc (num_regs + 1)); + dwarf_par_index = num_regs; + + from = current_frame_info.last_reg_to_save + 1; + to = current_frame_info.last_reg_to_save; + word_cnt = 0; + + for (i = current_frame_info.last_reg_to_save; i >= 0;) + { + if (!current_frame_info.save_regs[i] || 0 == i || split_here) + { + /* This block of regs is pushed in one instruction. */ + if (0 == i && current_frame_info.save_regs[i]) + from = 0; + + for (j = to; j >= from; --j) + { + if (j < CR16_FIRST_DWORD_REGISTER) + { + mode = HImode; + inc = 1; + } + else + { + mode = SImode; + inc = 2; + } + reg = gen_rtx_REG (mode, j); + offset += 2 * inc; + tmp = gen_rtx_SET (VOIDmode, + gen_frame_mem (mode, + plus_constant + (stack_pointer_rtx, + total_push_bytes - offset)), + reg); + RTX_FRAME_RELATED_P (tmp) = 1; + XVECEXP (dwarf, 0, dwarf_par_index--) = tmp; + } + from = i; + to = --i; + split_here = 0; + word_cnt = 0; + continue; + } + + if (i != RETURN_ADDRESS_REGNUM) + { + inc = (i < CR16_FIRST_DWORD_REGISTER) ? 1 : 2; + if (word_cnt + inc >= MAX_COUNT || FRAME_POINTER_REGNUM == i) + { + split_here = 1; + from = i; + continue; + } + word_cnt += inc; + } + + from = i--; + } + + tmp = gen_rtx_SET (SImode, stack_pointer_rtx, + gen_rtx_PLUS (SImode, stack_pointer_rtx, + GEN_INT (-offset))); + RTX_FRAME_RELATED_P (tmp) = 1; + XVECEXP (dwarf, 0, 0) = tmp; + + add_reg_note (insn, REG_FRAME_RELATED_EXPR, dwarf); +} + +/* +CompactRISC CR16 Architecture stack layout: + + 0 +--------------------- + | + . + . + | + +==================== Sp (x) = Ap (x+1) + A | Args for functions + | | called by X and Dynamically + | | Dynamic allocations allocated and + | | (alloca, variable deallocated + Stack | length arrays). + grows +-------------------- Fp (x) + down| | Local variables of X + ward| +-------------------- + | | Regs saved for X-1 + | +==================== Sp (x-1) = Ap (x) + | Args for func X + | pushed by X-1 + +-------------------- Fp (x-1) + | + | + V +*/ +void +cr16_expand_prologue (void) +{ + rtx insn; + + cr16_compute_frame (); + cr16_compute_save_regs (); + + /* If there is no need in push and adjustment to sp, return. */ + if ((current_frame_info.total_size + current_frame_info.reg_size) == 0) + return; + + if (current_frame_info.last_reg_to_save != -1) + { + /* If there are registers to push. */ + insn = emit_insn (gen_push_for_prologue + (GEN_INT (current_frame_info.reg_size))); + cr16_create_dwarf_for_multi_push (insn); + RTX_FRAME_RELATED_P (insn) = 1; + } + + + if (current_frame_info.total_size > 0) + { + insn = emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx, + GEN_INT (-current_frame_info.total_size))); + RTX_FRAME_RELATED_P (insn) = 1; + } + + if (frame_pointer_needed) + { + /* Initialize the frame pointer with the value of the stack pointer + pointing now to the locals. */ + insn = emit_move_insn (frame_pointer_rtx, stack_pointer_rtx); + } +} + +/* Generate insn that updates the stack for local variables and padding + for registers we save. - Generate the appropriate return insn. */ +void +cr16_expand_epilogue (void) +{ + rtx insn; + + /* Nonzero if we need to return and pop only RA. This will generate a + different insn. This differentiate is for the peepholes for call as + last statement in function. */ + int only_popret_RA = (current_frame_info.save_regs[RETURN_ADDRESS_REGNUM] + && (current_frame_info.reg_size + == CR16_UNITS_PER_DWORD)); + + if (frame_pointer_needed) + { + /* Restore the stack pointer with the frame pointers value. */ + insn = emit_move_insn (stack_pointer_rtx, frame_pointer_rtx); + } + + if (current_frame_info.total_size > 0) + { + insn = emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx, + GEN_INT (current_frame_info.total_size))); + RTX_FRAME_RELATED_P (insn) = 1; + } + + if (crtl->calls_eh_return) + { + /* Add this here so that (r5, r4) is actually loaded with the adjustment + value; otherwise, the load might be optimized away... + NOTE: remember to subtract the adjustment before popping the regs + and add it back before returning. */ + insn = emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx, + EH_RETURN_STACKADJ_RTX)); + } + + if (cr16_interrupt_function_p ()) + { + insn = emit_jump_insn (gen_interrupt_return ()); + RTX_FRAME_RELATED_P (insn) = 1; + } + else if (crtl->calls_eh_return) + { + /* Special case, pop what's necessary, adjust SP and jump to (RA). */ + insn = emit_jump_insn (gen_pop_and_popret_return + (GEN_INT (current_frame_info.reg_size))); + RTX_FRAME_RELATED_P (insn) = 1; + } + else if (current_frame_info.last_reg_to_save == -1) + /* Nothing to pop. */ + /* Don't output jump for interrupt routine, only retx. */ + emit_jump_insn (gen_jump_return ()); + else if (only_popret_RA) + { + insn = emit_jump_insn (gen_popret_RA_return ()); + RTX_FRAME_RELATED_P (insn) = 1; + } + else + { + insn = emit_jump_insn (gen_pop_and_popret_return + (GEN_INT (current_frame_info.reg_size))); + RTX_FRAME_RELATED_P (insn) = 1; + } +} + +/* Implements FRAME_POINTER_REQUIRED. */ +static bool +cr16_frame_pointer_required (void) +{ + return (cfun->calls_alloca || crtl->calls_eh_return + || cfun->has_nonlocal_label || crtl->calls_eh_return); +} + +static bool +cr16_can_eliminate (const int from ATTRIBUTE_UNUSED, const int to) +{ + return (to == STACK_POINTER_REGNUM ? !frame_pointer_needed : true); +} + + +/* A C compound statement that attempts to replace X with + a valid memory address for an operand of mode MODE. WIN + will be a C statement label elsewhere in the code. + X will always be the result of a call to break_out_memory_refs (), + and OLDX will be the operand that was given to that function to + produce X. + The code generated by this macro should not alter the + substructure of X. If it transforms X into a more legitimate form, + it should assign X (which will always be a C variable) a new value. */ +static rtx +cr16_legitimize_address (rtx x, rtx orig_x ATTRIBUTE_UNUSED, + enum machine_mode mode ATTRIBUTE_UNUSED) +{ + if (flag_pic) + return legitimize_pic_address (orig_x, mode, NULL_RTX); + else + return x; +} + +/* Implement TARGET_LEGITIMATE_CONSTANT_P + Nonzero if X is a legitimate constant for an immediate + operand on the target machine. You can assume that X + satisfies CONSTANT_P. In cr16c treat legitimize float + constant as an immediate operand. */ +static bool +cr16_legitimate_constant_p (enum machine_mode mode ATTRIBUTE_UNUSED, + rtx x ATTRIBUTE_UNUSED) +{ + return 1; +} + +void +notice_update_cc (rtx exp) +{ + if (GET_CODE (exp) == SET) + { + /* Jumps do not alter the cc's. */ + if (SET_DEST (exp) == pc_rtx) + return; + + /* Moving register or memory into a register: + it doesn't alter the cc's, but it might invalidate + the RTX's which we remember the cc's came from. + (Note that moving a constant 0 or 1 MAY set the cc's). */ + if (REG_P (SET_DEST (exp)) + && (REG_P (SET_SRC (exp)) || GET_CODE (SET_SRC (exp)) == MEM)) + { + return; + } + + /* Moving register into memory doesn't alter the cc's. + It may invalidate the RTX's which we remember the cc's came from. */ + if (GET_CODE (SET_DEST (exp)) == MEM && REG_P (SET_SRC (exp))) + { + return; + } + } + + CC_STATUS_INIT; + return; +} + +static enum machine_mode +cr16_unwind_word_mode (void) +{ + return SImode; +} + +/* Helper function for md file. This function is used to emit arithmetic + DI instructions. The argument "num" decides which instruction to be + printed. */ +const char * +cr16_emit_add_sub_di (rtx *operands, enum rtx_code code) +{ + rtx lo_op[2] ; + rtx hi0_op[2] ; + rtx hi1_op[2] ; + + lo_op[0] = gen_lowpart (SImode, operands[0]); + hi0_op[0] = simplify_gen_subreg (HImode, operands[0], DImode, 4); + hi1_op[0] = simplify_gen_subreg (HImode, operands[0], DImode, 6); + + lo_op[1] = gen_lowpart (SImode, operands[2]); + hi0_op[1] = simplify_gen_subreg (HImode, operands[2], DImode, 4); + hi1_op[1] = simplify_gen_subreg (HImode, operands[2], DImode, 6); + + switch (code) + { + case PLUS: + { + output_asm_insn ("addd\t%1, %0", lo_op) ; + output_asm_insn ("addcw\t%1, %0", hi0_op) ; + output_asm_insn ("addcw\t%1, %0", hi1_op) ; + break; + } + case MINUS: + { + output_asm_insn ("subd\t%1, %0", lo_op) ; + output_asm_insn ("subcw\t%1, %0", hi0_op) ; + output_asm_insn ("subcw\t%1, %0", hi1_op) ; + break; + } + default: + break; + } + + return ""; +} + + +/* Helper function for md file. This function is used to emit logical + DI instructions. The argument "num" decides which instruction to be + printed. */ +const char * +cr16_emit_logical_di (rtx *operands, enum rtx_code code) +{ + rtx lo_op[2] ; + rtx hi_op[2] ; + + lo_op[0] = gen_lowpart (SImode, operands[0]); + hi_op[0] = simplify_gen_subreg (SImode, operands[0], DImode, 4); + + lo_op[1] = gen_lowpart (SImode, operands[2]); + hi_op[1] = simplify_gen_subreg (SImode, operands[2], DImode, 4); + + switch (code) + { + case AND: + { + output_asm_insn ("andd\t%1, %0", lo_op) ; + output_asm_insn ("andd\t%1, %0", hi_op) ; + return ""; + } + case IOR: + { + output_asm_insn ("ord\t%1, %0", lo_op) ; + output_asm_insn ("ord\t%1, %0", hi_op) ; + return ""; + } + case XOR: + { + output_asm_insn ("xord\t%1, %0", lo_op) ; + output_asm_insn ("xord\t%1, %0", hi_op) ; + return ""; + } + default: + break; + } + + return ""; +} + +/* Initialize 'targetm' variable which contains pointers to functions + and data relating to the target machine. */ + +struct gcc_target targetm = TARGET_INITIALIZER; diff --git a/gcc/config/cr16/cr16.h b/gcc/config/cr16/cr16.h new file mode 100644 index 00000000000..01577ca5448 --- /dev/null +++ b/gcc/config/cr16/cr16.h @@ -0,0 +1,590 @@ +/* Definitions of target machine for GNU compiler, for CR16. + Copyright (C) 2012 Free Software Foundation, Inc. + Contributed by KPIT Cummins Infosystems Limited. + + This file is part of GCC. + + GCC is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 3, or (at your + option) any later version. + + GCC is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + License for more details. + + You should have received a copy of the GNU General Public License + along with GCC; see the file COPYING3. If not see + <http://www.gnu.org/licenses/>. */ + +#ifndef GCC_CR16_H +#define GCC_CR16_H + +#define OBJECT_FORMAT_ELF + +/* Controlling the driver. */ + +/* The GNU C++ standard library requires that these macros be defined. */ +#undef CPLUSPLUS_CPP_SPEC +#define CPLUSPLUS_CPP_SPEC "-D_GNU_SOURCE %(cpp)" + +#undef STARTFILE_SPEC +#define STARTFILE_SPEC "crt1.o%s crti.o%s crtbegin.o%s crtlibid.o%s" + +#undef ENDFILE_SPEC +#define ENDFILE_SPEC "crtend.o%s crtn.o%s" + +#undef MATH_LIBRARY +#define MATH_LIBRARY "" + +#undef LIB_SPEC +#define LIB_SPEC "-( -lc %{msim*:-lsim}%{!msim*:-lnosys} -) \ +%{msim*:%{!T*:-Tsim.ld}} \ +%{!T*:%{!msim*: %{-Telf32cr16.x}}}" + +/* Run-time target specification. */ +#ifndef TARGET_CPU_CPP_BUILTINS +#define TARGET_CPU_CPP_BUILTINS() \ +do \ + { \ + builtin_define ("__CR__"); \ + builtin_define ("__CR16__"); \ + builtin_define ("__CR16C__"); \ + if (TARGET_CR16CP) \ + builtin_define ("__CR16CP__"); \ + else \ + builtin_define ("__CR16CSTD__"); \ + if (CR16_TARGET_DATA_NEAR) \ + builtin_define ("__DATA_NEAR__"); \ + if (CR16_TARGET_DATA_MEDIUM) \ + builtin_define ("__DATA_MEDIUM__"); \ + if (CR16_TARGET_DATA_FAR) \ + builtin_define ("__DATA_FAR__"); \ + if (TARGET_INT32) \ + builtin_define ("__INT32__"); \ + } \ +while (0) +#endif + +/* Force the generation of dwarf .debug_frame sections even if not + compiling -g. This guarantees that we can unwind the stack. */ +#define DWARF2_FRAME_INFO 1 + +#define PREFERRED_DEBUGGING_TYPE DWARF2_DEBUG + +/* Generate .file/.loc directives, so that the assembler generates the + line table. */ +#define DWARF2_ASM_LINE_DEBUG_INFO 1 + +#define CR16_TARGET_DATA_NEAR cr16_is_data_model (DM_NEAR) +#define CR16_TARGET_DATA_MEDIUM cr16_is_data_model (DM_DEFAULT) +#define CR16_TARGET_DATA_FAR cr16_is_data_model (DM_FAR) + +/* Storage layout. */ +#define BITS_BIG_ENDIAN 0 + +#define BYTES_BIG_ENDIAN 0 + +#define WORDS_BIG_ENDIAN 0 + +#define UNITS_PER_WORD 2 + +/* Units per 32-bit (DWORD). */ +#define CR16_UNITS_PER_DWORD 4 + +#define POINTER_SIZE 32 + +#define PARM_BOUNDARY 16 + +#define STACK_BOUNDARY (MAX (BIGGEST_ALIGNMENT, PARM_BOUNDARY)) + +#define FUNCTION_BOUNDARY BIGGEST_ALIGNMENT + +/* Biggest alignment on CR16C+ is 32-bit as internal bus is AMBA based + where as CR16C is proprietary internal bus architecture. */ +#define BIGGEST_ALIGNMENT ((TARGET_CR16CP) ? 32 : 16) + +#define MAX_FIXED_MODE_SIZE 64 + +/* In CR16 arrays of chars are word-aligned, so strcpy () will be faster. */ +#define DATA_ALIGNMENT(TYPE, ALIGN) \ + (((TREE_CODE (TYPE) == ARRAY_TYPE) \ + && (TYPE_MODE (TREE_TYPE (TYPE)) == QImode) \ + && ((ALIGN) < BITS_PER_WORD)) \ + ? (BITS_PER_WORD) : (ALIGN)) + +/* In CR16 strings are word-aligne; strcpy from constants will be faster. */ +#define CONSTANT_ALIGNMENT(CONSTANT, ALIGN) \ + (((TREE_CODE (CONSTANT) == STRING_CST) && ((ALIGN) < BITS_PER_WORD)) \ + ? (BITS_PER_WORD) : (ALIGN)) + +#define STRICT_ALIGNMENT 0 + +#define PCC_BITFIELD_TYPE_MATTERS 1 + +/* Layout of source language data types. */ +#define INT_TYPE_SIZE (TARGET_INT32 ? 32 : 16) + +#define SHORT_TYPE_SIZE 16 + +#define LONG_TYPE_SIZE 32 + +#define LONG_LONG_TYPE_SIZE 64 + +#define FLOAT_TYPE_SIZE 32 + +#define DOUBLE_TYPE_SIZE 64 + +#define LONG_DOUBLE_TYPE_SIZE 64 + +#define DEFAULT_SIGNED_CHAR 1 + +#define SIZE_TYPE "long unsigned int" + +#define PTRDIFF_TYPE "long int" + +#define WCHAR_TYPE "short unsigned int" + +#define WCHAR_TYPE_SIZE 16 + +/* By default, the C++ compiler will use the lowest bit of the pointer + to function to indicate a pointer-to-member-function points to a + virtual member function. However, in CR architecture FUNCTION_BOUNDARY + indicates function addresses are always even, but function pointers can be + odd (after right-shifting them when loading them into a register), and the + default doesn't work. In that case, the lowest bit of the delta + field will be used (the remainder of the field is shifted to the left). */ +#define TARGET_PTRMEMFUNC_VBIT_LOCATION ptrmemfunc_vbit_in_delta + +/* Define DEFAULT_PCC_STRUCT_RETURN to 1 if all structure and union return + values must be in memory. */ +#define DEFAULT_PCC_STRUCT_RETURN 0 + +/* Register usage. */ + +/* First 32-bit register is R12. */ +#define CR16_FIRST_DWORD_REGISTER 12 + +#define FIRST_PSEUDO_REGISTER 16 + +/* 1 for registers that have pervasive standard uses + and are not available for the register allocator. + On the CR16, only the stack pointer (r15) is such. */ +#define FIXED_REGISTERS \ + { \ + /* r0 r1 r2 r3 r4 r5 r6 r7 r8 r9 r10. */ \ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \ + /* r11 r12 r13 ra sp. */ \ + 0, 0, 0, 0, 1 \ + } + +/* 1 for registers not available across function calls. + These must include the FIXED_REGISTERS and also any + registers that can be used without being saved. + The latter must include the registers where values are returned + and the register where structure-value addresses are passed. + + On the CR16, calls clobbers r0-r6 (scratch registers), + ra (the return address) and sp (the stack pointer). */ +#define CALL_USED_REGISTERS \ + { \ + /* r0 r1 r2 r3 r4 r5 r6 r7 r8 r9 r10. */ \ + 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, \ + /* r11 r12 r13 ra sp. */ \ + 0, 0, 0, 1, 1 \ + } + +/* Returns 1 if the register is longer than word size, 0 otherwise. */ +#define LONG_REG_P(REGNO) \ + (HARD_REGNO_NREGS (REGNO, \ + GET_MODE_WIDER_MODE (smallest_mode_for_size \ + (BITS_PER_WORD, MODE_INT))) == 1) + +#define HARD_REGNO_NREGS(REGNO, MODE) \ + ((REGNO >= CR16_FIRST_DWORD_REGISTER) \ + ? ((GET_MODE_SIZE (MODE) + CR16_UNITS_PER_DWORD - 1) / CR16_UNITS_PER_DWORD)\ + : ((GET_MODE_SIZE (MODE) + UNITS_PER_WORD - 1) / UNITS_PER_WORD)) + +/* Nonzero if it is permissible to store a value of mode @var{mode} in hard + register number @var{regno} (or in several registers starting with that + one). On the CR16 architecture, all registers can hold all modes, + except that double precision floats (and double ints) must fall on + even-register boundaries. */ +#define HARD_REGNO_MODE_OK(REGNO, MODE) cr16_hard_regno_mode_ok (REGNO, MODE) + +#define NOTICE_UPDATE_CC(EXP, INSN) \ + notice_update_cc ((EXP)) + +/* Interrupt functions can only use registers that have already been + saved by the prologue, even if they would normally be call-clobbered + Check if sizes are same and then check if it is possible to rename. */ +#define HARD_REGNO_RENAME_OK(SRC, DEST) \ + (!cr16_interrupt_function_p () || (df_regs_ever_live_p (DEST))) + +/* Exception handling stuff. */ + +/*To ensure correct dwarf unwinding. */ +#define LIBGCC2_UNWIND_ATTRIBUTE __attribute__((optimize ("no-gcse","no-dse"))) + +#define gen_rtx_RA gen_rtx_REG (Pmode, RETURN_ADDRESS_REGNUM) + +/* Use (r8,r7) and (r10,r9) to pass exception handling information. */ +#define EH_RETURN_DATA_REGNO(N) (((N) < 2) ? (N*2 + 7) : INVALID_REGNUM) + +#define DWARF2_UNWIND_INFO 1 + +/* (r5,r4) holds a stack adjustment for returning to a handler. */ +#define EH_RETURN_STACKADJ_RTX gen_rtx_REG (Pmode, 4) + +#define EH_RETURN_HANDLER_RTX \ + gen_rtx_MEM (Pmode, plus_constant (arg_pointer_rtx, -4)) + +#define INCOMING_RETURN_ADDR_RTX gen_rtx_RA + +#define DWARF_FRAME_RETURN_COLUMN \ + DWARF_FRAME_REGNUM (RETURN_ADDRESS_REGNUM) + +#define INCOMING_FRAME_SP_OFFSET 0 +#define FRAME_POINTER_CFA_OFFSET(FNDECL) 0 + +/* A C expression whose value is RTL representing the value of the return + address for the frame COUNT steps up from the current frame. */ +#define RETURN_ADDR_RTX(COUNT, FRAME) \ + (0 == COUNT) ? gen_rtx_PLUS (Pmode, gen_rtx_RA, gen_rtx_RA) \ + : const0_rtx + +#define MODES_TIEABLE_P(MODE1, MODE2) \ + (GET_MODE_CLASS (MODE1) == GET_MODE_CLASS (MODE2)) + +enum reg_class +{ + NO_REGS, + SHORT_REGS, + LONG_REGS, + NOSP_REGS, + DOUBLE_BASE_REGS, + GENERAL_REGS, + ALL_REGS, + LIM_REG_CLASSES +}; + +#define N_REG_CLASSES (int) LIM_REG_CLASSES + +#define REG_CLASS_NAMES \ + { \ + "NO_REGS", \ + "SHORT_REGS", \ + "LONG_REGS", \ + "NOSP_REGS", \ + "DOUBLE_BASE_REGS", \ + "GENERAL_REGS", \ + "ALL_REGS" \ + } + +#define REG_CLASS_CONTENTS \ + { \ + {0x00000000}, /* NO_REGS */ \ + {0x00000FFF}, /* SHORT_REGS : 0 - 11 */ \ + {0x0000F000}, /* LONG_REGS : 12 - 15 */ \ + {0x00007FFF}, /* NOSP_REGS : 0 - 14 */ \ + {0x0000F555}, /* DOUBLE_BASE_REGS : 2,4,6,8,10 */ \ + {0x0000FFFF}, /* GENERAL_REGS : 0 - 15 */ \ + {0x0000FFFF} /* ALL_REGS : 0 - 15 */ \ + } + +#define TARGET_SMALL_REGISTER_CLASSES_FOR_MODE_P hook_bool_mode_true + +#define REGNO_REG_CLASS(REGNO) cr16_regno_reg_class (REGNO) + +#define BASE_REG_CLASS GENERAL_REGS + +#define MODE_BASE_REG_CLASS(MODE) \ + (GET_MODE_SIZE (MODE) <= 4 ? (BASE_REG_CLASS) : (DOUBLE_BASE_REGS)) + +#define INDEX_REG_CLASS LONG_REGS + +#define CR16_REGNO_OK_FOR_BASE_P(REGNO) \ + (((REGNO) < FIRST_PSEUDO_REGISTER) \ + || (reg_renumber && ((unsigned) reg_renumber[REGNO] \ + < FIRST_PSEUDO_REGISTER))) + +/* Use even-numbered reg for 64-bit accesses. */ +#define REGNO_MODE_OK_FOR_BASE_P(REGNO, MODE) \ + (CR16_REGNO_OK_FOR_BASE_P(REGNO) && \ + ((GET_MODE_SIZE (MODE) > 4 && \ + (REGNO) < CR16_FIRST_DWORD_REGISTER) \ + ? (0 == ((REGNO) & 1)) \ + : 1)) + +/* TODO: For now lets not support index addressing mode. */ +#define REGNO_OK_FOR_INDEX_P(REGNO) \ + (((REGNO >= CR16_FIRST_DWORD_REGISTER) \ + && ((REGNO) < FIRST_PSEUDO_REGISTER)) \ + || (reg_renumber \ + && (((unsigned) reg_renumber[REGNO] >= CR16_FIRST_DWORD_REGISTER) \ + && ((unsigned) reg_renumber[REGNO] < FIRST_PSEUDO_REGISTER))) \ + ) + +#define PREFERRED_RELOAD_CLASS(X, CLASS) CLASS + +/* The maximum number of consecutive registers of class CLASS needed to + hold a value of mode MODE. + On the CompactRISC architecture, the size of MODE in words. + The size of MODE in double words for the class LONG_REGS. + + The following check assumes if the class is not LONG_REGS, then + all (NO_REGS, SHORT_REGS, NOSP_REGS and GENERAL_REGS) other classes are + short. We may have to check if this can cause any degradation in + performance. */ +#define CLASS_MAX_NREGS(CLASS, MODE) \ + (CLASS == LONG_REGS \ + ? (GET_MODE_SIZE (MODE) + CR16_UNITS_PER_DWORD - 1) / CR16_UNITS_PER_DWORD\ + : (GET_MODE_SIZE (MODE) + UNITS_PER_WORD - 1) / UNITS_PER_WORD) + +/* Macros to check the range of integers . These macros were used across + the port, majorly in constraints.md, predicates.md files. */ +#define SIGNED_INT_FITS_N_BITS(imm, N) \ + ((((imm) < ((HOST_WIDE_INT) 1 << ((N) - 1))) \ + && ((imm) >= -((HOST_WIDE_INT) 1 << ((N) - 1)))) ? 1 : 0) + +#define UNSIGNED_INT_FITS_N_BITS(imm, N) \ + (((imm) < ((HOST_WIDE_INT) 1 << (N)) && (imm) >= (HOST_WIDE_INT) 0) ? 1 : 0) + +#define IN_RANGE_P(VALUE, LOW, HIGH) \ + ((((HOST_WIDE_INT)(VALUE)) >= (HOST_WIDE_INT)(LOW)) \ + && (((HOST_WIDE_INT)(VALUE)) <= ((HOST_WIDE_INT)(HIGH)))) + +#define IN_RAN(VALUE, LOW, HIGH) \ + (((((HOST_WIDE_INT)(VALUE)) >= (HOST_WIDE_INT)(LOW)) \ + && (((HOST_WIDE_INT)(VALUE)) <= ((HOST_WIDE_INT)(HIGH)))) ? 1 : 0) + +/* This check is for sbit/cbit instruction. */ +#define OK_FOR_Z(OP) \ + ((GET_CODE (OP) == MEM && GET_CODE (XEXP (OP, 0)) == CONST_INT) \ + || (GET_CODE (OP) == MEM && GET_CODE (XEXP (OP, 0)) == REG) \ + || (GET_CODE (OP) == MEM && GET_CODE (XEXP (OP, 0)) == PLUS \ + && GET_CODE (XEXP ((XEXP (OP, 0)), 0)) == REG \ + && GET_CODE (XEXP ((XEXP (OP, 0)), 1)) == CONST_INT)) + +/* Stack layout and calling conventions. */ +#define STACK_GROWS_DOWNWARD + +#define STARTING_FRAME_OFFSET 0 + +#define STACK_POINTER_REGNUM 15 + +#define FRAME_POINTER_REGNUM 13 + +#define ARG_POINTER_REGNUM 12 + +#define STATIC_CHAIN_REGNUM 1 + +#define RETURN_ADDRESS_REGNUM 14 + +#define FIRST_PARM_OFFSET(FNDECL) 0 + +#define ELIMINABLE_REGS \ + { \ + { ARG_POINTER_REGNUM, STACK_POINTER_REGNUM}, \ + { ARG_POINTER_REGNUM, FRAME_POINTER_REGNUM}, \ + { FRAME_POINTER_REGNUM, STACK_POINTER_REGNUM} \ + } + +#define INITIAL_ELIMINATION_OFFSET(FROM, TO, OFFSET) \ + do \ + { \ + (OFFSET) = cr16_initial_elimination_offset ((FROM), (TO)); \ + } \ + while (0) + +/* Passing function arguments. */ + +#define ACCUMULATE_OUTGOING_ARGS 0 + +#define PUSH_ARGS 1 + +#define PUSH_ROUNDING(BYTES) (((BYTES) + 1) & ~1) + +#ifndef CUMULATIVE_ARGS +struct cumulative_args +{ + int ints; + int last_parm_in_reg; +}; + +#define CUMULATIVE_ARGS struct cumulative_args +#endif + +/* On the CR16 architecture, Varargs routines should receive their parameters + on the stack. */ + +#define INIT_CUMULATIVE_ARGS(CUM, FNTYPE, LIBNAME, FNDECL, N_NAMED_ARGS) \ + cr16_init_cumulative_args (&(CUM), (FNTYPE), (LIBNAME)) + +#define FUNCTION_ARG_REGNO_P(REGNO) cr16_function_arg_regno_p (REGNO) + +/* Generating code for profiling - NOT IMPLEMENTED. */ +#undef FUNCTION_PROFILER +#define FUNCTION_PROFILER(STREAM, LABELNO) \ +{ \ + sorry ("profiler support for CR16"); \ +} + +/* Trampolines for nested functions - NOT SUPPORTED. */ +#define TRAMPOLINE_SIZE 16 + +/* ADDRESSING MODES. */ + +#define CONSTANT_ADDRESS_P(X) \ + (GET_CODE (X) == LABEL_REF \ + || GET_CODE (X) == SYMBOL_REF \ + || GET_CODE (X) == CONST \ + || GET_CODE (X) == CONST_INT) + +#define MAX_REGS_PER_ADDRESS 2 + +#define HAVE_POST_INCREMENT 0 +#define HAVE_POST_DECREMENT 0 +#define HAVE_POST_MODIFY_DISP 0 +#define HAVE_POST_MODIFY_REG 0 + +#ifdef REG_OK_STRICT +#define CR16_REG_OK_FOR_BASE_P(X) CR16_REGNO_OK_FOR_BASE_P (REGNO (X)) +#define REG_MODE_OK_FOR_BASE_P(X, MODE) \ + REGNO_MODE_OK_FOR_BASE_P (REGNO(X), MODE) +#define REG_OK_FOR_INDEX_P(X) REGNO_OK_FOR_INDEX_P (REGNO (X)) +#else /* not REG_OK_STRICT. */ +#define CR16_REG_OK_FOR_BASE_P(X) 1 +#define REG_MODE_OK_FOR_BASE_P(X, MODE) 1 +#define REG_OK_FOR_INDEX_P(X) 1 +#endif /* not REG_OK_STRICT. */ + +/* Go to LABEL if ADDR (a legitimate address expression) has + an effect that depends on the machine mode it is used for. */ +#define GO_IF_MODE_DEPENDENT_ADDRESS(ADDR, LABEL) + +/* Assume best case (branch predicted). */ +#define BRANCH_COST(speed_p, predictable_p) 2 + +#define SLOW_BYTE_ACCESS 1 + +/* It is as good or better to call a constant function address than to + call an address kept in a register. */ +#define NO_FUNCTION_CSE + +/* Dividing the output into sections. */ + +#define TEXT_SECTION_ASM_OP "\t.section\t.text" + +#define DATA_SECTION_ASM_OP "\t.section\t.data" + +#define BSS_SECTION_ASM_OP "\t.section\t.bss" + +/* Position independent code (PIC). */ +/* NEAR_PIC for -fpic option. */ + +#define NEAR_PIC 1 + +/* FAR_PIC for -fPIC option. */ + +#define FAR_PIC 2 + +#define PIC_OFFSET_TABLE_REGNUM 12 + +#define LEGITIMATE_PIC_OPERAND_P(X) legitimate_pic_operand_p (X) + +/* Assembler format. */ + +/* Character to start a comment. */ +#define ASM_COMMENT_START "#" + +#define GLOBAL_ASM_OP "\t.globl\t" + +#undef USER_LABEL_PREFIX +#define USER_LABEL_PREFIX "_" + +#undef ASM_OUTPUT_LABELREF +#define ASM_OUTPUT_LABELREF(STREAM, NAME) \ + asm_fprintf (STREAM, "%U%s", (*targetm.strip_name_encoding) (NAME)); + +#define ASM_OUTPUT_SYMBOL_REF(STREAM, SYMBOL) \ + do \ + { \ + const char *rn = XSTR (SYMBOL, 0); \ + assemble_name (STREAM, rn); \ + if (SYMBOL_REF_FUNCTION_P (SYMBOL)) \ + { \ + fprintf ((STREAM), "@c"); \ + } \ + } \ + while (0) + +#undef ASM_APP_ON +#define ASM_APP_ON "#APP\n" + +#undef ASM_APP_OFF +#define ASM_APP_OFF "#NO_APP\n" + +/* Switch into a generic section. */ +#define TARGET_ASM_NAMED_SECTION default_elf_asm_named_section + +#undef INIT_SECTION_ASM_OP +#define INIT_SECTION_ASM_OP "\t.section\t.init" + +#undef FINI_SECTION_ASM_OP +#define FINI_SECTION_ASM_OP "\t.section\t.fini" + +/* Instruction output. */ + +#define REGISTER_NAMES \ + { \ + "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", \ + "r8", "r9", "r10", "r11", "r12", "r13", "ra", "sp" \ + } + +/* Output of dispatch tables. */ + +/* Revisit. No PC relative case as label expressions are not + properly supported in binutils else we could have done this: + #define CASE_VECTOR_PC_RELATIVE (optimize_size ? 1 : 0). */ +#define CASE_VECTOR_PC_RELATIVE 0 + +#define ASM_OUTPUT_ADDR_DIFF_ELT(FILE, BODY, VALUE, REL) \ + ((GET_MODE (BODY) == QImode) \ + ? fprintf ((FILE), "\t.byte (.L%d-.L%d) >> 1\n", \ + VALUE, REL) \ + : fprintf ((FILE), "\t.word (.L%d-.L%d) >> 1\n", \ + VALUE, REL)) + +#define ASM_OUTPUT_ADDR_VEC_ELT(STREAM, VALUE) \ + asm_fprintf ((STREAM), "\t.long\t.L%d@c\n", (VALUE)) + +/* Alignment in assembler file. */ + +#define ASM_OUTPUT_ALIGN(STREAM, POWER) \ + asm_fprintf ((STREAM), "\t.align\t%d\n", 1 << (POWER)) + +/* Miscellaneous parameters. */ + +#define CASE_VECTOR_MODE Pmode + +#define MOVE_MAX 4 + +#define TRULY_NOOP_TRUNCATION(OUTPREC, INPREC) 1 + +#define STORE_FLAG_VALUE 1 + +#define Pmode SImode + +#define FUNCTION_MODE QImode + +/* Define this boolean macro(s) to indicate whether or not your architecture + has (un)conditional branches that can span all of memory. It is used in + conjunction with an optimization that partitions hot and cold basic blocks + into separate sections of the executable. + CR16 contains branch instructions that span whole address space. */ +#define HAS_LONG_COND_BRANCH 1 +#define HAS_LONG_UNCOND_BRANCH 1 + +#endif /* End of GCC_CR16_H. */ diff --git a/gcc/config/cr16/cr16.md b/gcc/config/cr16/cr16.md new file mode 100644 index 00000000000..5e4530c32ce --- /dev/null +++ b/gcc/config/cr16/cr16.md @@ -0,0 +1,1084 @@ +;; GCC machine description for CR16. +;; Copyright (C) 2012 Free Software Foundation, Inc. +;; Contributed by KPIT Cummins Infosystems Limited. + +;; This file is part of GCC. + +;; GCC is free software; you can redistribute it and/or modify it +;; under the terms of the GNU General Public License as published by +;; the Free Software Foundation; either version 3, or (at your option) +;; any later version. + +;; GCC is distributed in the hope that it will be useful, but WITHOUT +;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +;; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +;; License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with GCC; see the file COPYING3. If not see +;; <http://www.gnu.org/licenses/>. + +;; Register numbers +(define_constants + [(SP_REGNUM 15); Stack pointer + (RA_REGNUM 14); Return address + ] +) + +;; Predicates & Constraints +(include "predicates.md") +(include "constraints.md") + +;; UNSPEC usage +(define_constants + [(UNSPEC_PIC_ADDR 0) + (UNSPEC_PIC_LOAD_ADDR 1) + (UNSPEC_LIBRARY_OFFSET 2) + (UNSPEC_SH_LIB_PUSH_R12 3) + (UNSPEC_SH_LIB_POP_R12 4) + (UNSPEC_RETURN_ADDR 5) + ] +) + +;; Attributes +(define_attr "length" "" (const_int 2)) + +(define_asm_attributes + [(set_attr "length" "2")] +) + +;; Mode Macro Definitions +(define_mode_iterator CR16IM [QI HI SI]) +(define_mode_iterator LONG [SI SF]) +(define_mode_iterator ALLMTD [QI HI SI SF DI DF]) +(define_mode_iterator DOUBLE [DI DF]) +(define_mode_iterator SHORT [QI HI]) +(define_mode_attr tIsa [(QI "b") (HI "w") (SI "d") (SF "d")]) +(define_mode_attr lImmArith [(QI "4") (HI "4") (SI "6") (SF "6")]) +(define_mode_attr lImmArithD [(QI "4") (HI "4") (SI "6") (SF "6") (DI "12") (DF "12")]) +(define_mode_attr iF [(QI "i") (HI "i") (SI "i") (SF "F")]) +(define_mode_attr iFD [(DI "i") (DF "F")]) +(define_mode_attr LL [(QI "L") (HI "L")]) +(define_mode_attr shImmBits [(QI "3") (HI "4") (SI "5")]) + +; In QI mode we push 2 bytes instead of 1 byte. +(define_mode_attr pushCnstr [(QI "X") (HI "<") (SI "<") (SF "<") (DI "<") (DF "<")]) + +; tpush will be used to generate the 'number of registers to push' in the +; push instruction. +(define_mode_attr tpush [(QI "1") (HI "1") (SI "2") (SF "2") (DI "4") (DF "4")]) + +;; Code Macro Definitions +(define_code_attr sIsa [(sign_extend "") (zero_extend "u")]) +(define_code_attr sPat [(sign_extend "s") (zero_extend "u")]) +(define_code_attr szPat [(sign_extend "") (zero_extend "zero_")]) +(define_code_attr szIsa [(sign_extend "x") (zero_extend "z")]) + +(define_code_iterator sz_xtnd [ sign_extend zero_extend]) +(define_code_iterator any_cond [eq ne gt gtu lt ltu ge geu le leu]) +(define_code_iterator plusminus [plus minus]) + +(define_code_attr plusminus_insn [(plus "add") (minus "sub")]) +(define_code_attr plusminus_flag [(plus "PLUS") (minus "MINUS")]) +(define_code_attr comm [(plus "%") (minus "")]) + +(define_code_iterator any_logic [and ior xor]) +(define_code_attr logic [(and "and") (ior "or") (xor "xor")]) +(define_code_attr any_logic_insn [(and "and") (ior "ior") (xor "xor")]) +(define_code_attr any_logic_flag [(and "AND") (ior "IOR") (xor "XOR")]) + +(define_mode_iterator QH [QI HI]) +(define_mode_attr qh [(QI "qi") (HI "hi")]) +(define_mode_attr QHsz [(QI "2,2,2") (HI "2,2,4")]) +(define_mode_attr QHsuffix [(QI "b") (HI "w")]) + + +;; Function Prologue and Epilogue +(define_expand "prologue" + [(const_int 0)] + "" + { + cr16_expand_prologue (); + DONE; + } +) + +(define_insn "push_for_prologue" + [(set (reg:SI SP_REGNUM) + (minus:SI (reg:SI SP_REGNUM) + (match_operand:SI 0 "immediate_operand" "i")))] + "reload_completed" + { + return cr16_prepare_push_pop_string (0); + } + [(set_attr "length" "4")] +) + +(define_expand "epilogue" + [(return)] + "" + { + cr16_expand_epilogue (); + DONE; + } +) + +(define_insn "pop_and_popret_return" + [(set (reg:SI SP_REGNUM) + (plus:SI (reg:SI SP_REGNUM) + (match_operand:SI 0 "immediate_operand" "i"))) + (use (reg:SI RA_REGNUM)) + (return)] + "reload_completed" + { + return cr16_prepare_push_pop_string (1); + } + [(set_attr "length" "4")] +) + +(define_insn "popret_RA_return" + [(use (reg:SI RA_REGNUM)) + (return)] + "reload_completed" + "popret\tra" + [(set_attr "length" "2")] +) + +;; Arithmetic Instuction Patterns + +;; Addition-Subtraction "adddi3/subdi3" insns. +(define_insn "<plusminus_insn>di3" + [(set (match_operand:DI 0 "register_operand" "=r") + (plusminus:DI (match_operand:DI 1 "register_operand" "<comm>0") + (match_operand:DI 2 "register_operand" "r")))] + "" + { + return cr16_emit_add_sub_di (operands, <plusminus_flag>); + }) + +(define_insn "addsi3" + [(set (match_operand:SI 0 "register_operand" "=r,r,r,r,r") + (plus:SI (match_operand:SI 1 "register_operand" "%0,0,0,0,0") + (match_operand:SI 2 "reg_si_int_operand" "r,M,N,O,i")))] + "" + "addd\t%2, %0" + [(set_attr "length" "2,2,4,4,6")] +) + +;; Addition-Subtraction "addhi3/subhi3" insns. +(define_insn "<plusminus_insn>hi3" + [(set (match_operand:HI 0 "register_operand" "=c,c,c") + (plusminus:HI (match_operand:HI 1 "register_operand" "<comm>0,0,0") + (match_operand:HI 2 "reg_hi_int_operand" "c,M,N")))] + "" + "<plusminus_insn>w\t%2, %0" + [(set_attr "length" "2,2,4")] +) + +;; Addition-Subtraction "addqi3/subqi3" insns. +(define_insn "<plusminus_insn>qi3" + [(set (match_operand:QI 0 "register_operand" "=c,c") + (plusminus:QI (match_operand:QI 1 "register_operand" "<comm>0,0") + (match_operand:QI 2 "reg_qi_int_operand" "c,M")))] + "" + "<plusminus_insn>b\t%2, %0" + [(set_attr "length" "2,2")] +) + +;; Subtract Instruction +(define_insn "subsi3" + [(set (match_operand:SI 0 "register_operand" "=r,r") + (minus:SI (match_operand:SI 1 "register_operand" "0,0") + (match_operand:SI 2 "reg_si_int_operand" "r,i")))] + "" + "subd\t%2, %0" + [(set_attr "length" "4,6")] +) + +;; Multiply and Accumulate Instructions "smachisi3/umachisi3" +(define_insn "<sPat>maddhisi4" + [(set (match_operand:SI 0 "register_operand" "=r") + (plus:SI + (mult:SI (sz_xtnd:SI (match_operand:HI 1 "register_operand" "r")) + (sz_xtnd:SI (match_operand:HI 2 "register_operand" "r"))) + (match_operand:SI 3 "register_operand" "0")))] + "TARGET_MAC" + "mac<sPat>w\t%1, %2, %0" + [(set_attr "length" "2")] +) + +;; Multiply Instructions +(define_insn "mulhi3" + [(set (match_operand:HI 0 "register_operand" "=c,c,c") + (mult:HI (match_operand:HI 1 "register_operand" "%0,0,0") + (match_operand:HI 2 "reg_or_int_operand" "c,M,N")))] + "" + "mulw\t%2, %0" + [(set_attr "length" "2,2,4")] +) + +(define_insn "mulqihi3" + [(set (match_operand:HI 0 "register_operand" "=c") + (mult:HI (sign_extend:HI (match_operand:QI 1 "register_operand" "%0")) + (sign_extend:HI (match_operand:QI 2 "register_operand" "c"))))] + "" + "mulsb\t%2, %0" + [(set_attr "length" "2")] +) + +;; Bit Set/Clear Instructions +(define_expand "insv" + [(set (zero_extract (match_operand 0 "memory_operand" "") + (match_operand 1 "immediate_operand" "") + (match_operand 2 "immediate_operand" "")) + (match_operand 3 "immediate_operand" ""))] + "TARGET_BIT_OPS" + { + if (INTVAL (operands[1]) != 1) + FAIL; + if (INTVAL (operands[2]) < 0 || INTVAL (operands[2]) > 15) + FAIL; + if (INTVAL (operands[3]) == 1) + { + if (GET_MODE (operands[0]) == QImode) + { + emit_insn (gen_set_bitqi (operands[0], operands[2])); + DONE; + } + else if (GET_MODE (operands[0]) == HImode) + { + emit_insn (gen_set_bithi (operands[0], operands[2])); + DONE; + } + } + if (INTVAL (operands[3]) == 0) + { + if (GET_MODE (operands[0]) == QImode) + { + emit_insn (gen_clr_bitqi (operands[0], operands[2])); + DONE; + } + else if (GET_MODE (operands[0]) == HImode) + { + emit_insn (gen_clr_bithi (operands[0], operands[2])); + DONE; + } + } + } +) + +(define_insn "set_bit<mode>" + [(set (zero_extract:SHORT (match_operand:SHORT 0 "memory_operand" "+m") + (const_int 1) + (match_operand 1 "immediate_operand" "i")) + (const_int 1))] + "TARGET_BIT_OPS" + "sbit<tIsa>\t%1,%0" + [(set_attr "length" "2")] +) + +(define_insn "clr_bit<mode>" + [(set (zero_extract:SHORT (match_operand:SHORT 0 "memory_operand" "+m") + (const_int 1) + (match_operand 1 "immediate_operand" "i")) + (const_int 0))] + "TARGET_BIT_OPS" + "cbit<tIsa>\t%1,%0" + [(set_attr "length" "2")] +) + +(define_insn "set_bit<mode>_mem" + [(set (match_operand:SHORT 0 "bit_operand" "=m") + (ior:SHORT (match_dup 0) + (match_operand:SHORT 1 "one_bit_operand" "i")) + )] + "TARGET_BIT_OPS" + "sbit<tIsa>\t$%s1,%0" + [(set_attr "length" "2")] +) + +(define_insn "clear_bit<mode>_mem" + [(set (match_operand:SHORT 0 "bit_operand" "=m") + (and:SHORT (match_dup 0) + (match_operand:SHORT 1 "rev_one_bit_operand" "i")) + )] + "TARGET_BIT_OPS" + "cbit<tIsa>\t$%r1,%0" + [(set_attr "length" "2")] +) + +;; Logical Instructions - and/ior/xor "anddi3/iordi3/xordi3" +(define_insn "<any_logic_insn>di3" + [(set (match_operand:DI 0 "register_operand" "=r") + (any_logic:DI (match_operand:DI 1 "register_operand" "%0") + (match_operand:DI 2 "register_operand" "r")))] + "" + { + return cr16_emit_logical_di (operands, <any_logic_flag>); + }) + +; Logical and/ior/xor "andsi3/iorsi3/xorsi3" +(define_insn "<any_logic_insn>si3" + [(set (match_operand:SI 0 "register_operand" "=r,r,r,r") + (any_logic:SI (match_operand:SI 1 "register_operand" "%0,0,0,0") + (match_operand:SI 2 "reg_si_int_operand" "r,M,N,i")))] + "" + "<logic>d\t%2, %0" + [(set_attr "length" "2,2,4,6")] +) + +; Logical and/ior/xor in HImode "andhi3/iorhi3/xorhi3" +; Logical and/ior/xor in QImode "andqi3/iorqi3/xorqi3" +(define_insn "<any_logic_insn><qh>3" + [(set (match_operand:QH 0 "register_operand" "=c,c,c") + (any_logic:QH (match_operand:QH 1 "register_operand" "%0,0,0") + (match_operand:QH 2 "reg_hi_int_operand" "c,M,N")))] + "" + "<logic><QHsuffix>\t%2, %0" + [(set_attr "length" "<QHsz>")] +) + +;; Sign and Zero Extend Instructions +(define_insn "<szPat>extendhisi2" + [(set (match_operand:SI 0 "register_operand" "=r") + (sz_xtnd:SI (match_operand:HI 1 "register_operand" "r")))] + "" + "mov<szIsa>w\t%1, %0" + [(set_attr "length" "4")] +) + +(define_insn "<szPat>extendqihi2" + [(set (match_operand:HI 0 "register_operand" "=r") + (sz_xtnd:HI (match_operand:QI 1 "register_operand" "r")))] + "" + "mov<szIsa>b\t%1, %0" + [(set_attr "length" "4")] +) + +;; One's Complement +(define_insn "one_cmpldi2" + [(set (match_operand:DI 0 "register_operand" "=r") + (not:DI (match_operand:DI 1 "register_operand" "0")))] + "" + { + rtx xoperand ; + int reg0 = REGNO (operands[0]); + + xoperand = gen_rtx_REG (SImode, reg0 + 2); + output_asm_insn ("xord\t$-1, %0", operands); + output_asm_insn ("xord\t$-1, %0", &xoperand); + return "" ; + } + [(set_attr "length" "12")] +) + +(define_insn "one_cmpl<mode>2" + [(set (match_operand:CR16IM 0 "register_operand" "=r") + (not:CR16IM (match_operand:CR16IM 1 "register_operand" "0")))] + "" + "xor<tIsa>\t$-1, %0" + [(set_attr "length" "2")] +) + +;; Arithmetic Left and Right Shift Instructions +(define_insn "ashlqi3" + [(set (match_operand:QI 0 "register_operand" "=c,c") + (ashift:QI (match_operand:QI 1 "register_operand" "0,0") + (match_operand:QI 2 "nonmemory_operand" "c,I")))] + "" + "ashub\t%2, %0" + [(set_attr "length" "2,2")] +) + +(define_insn "ashlhi3" + [(set (match_operand:HI 0 "register_operand" "=c,c") + (ashift:HI (match_operand:HI 1 "register_operand" "0,0") + (match_operand:QI 2 "nonmemory_operand" "c,J")))] + "" + "ashuw\t%2, %0" + [(set_attr "length" "2,2")] +) + +(define_insn "ashlsi3" + [(set (match_operand:SI 0 "register_operand" "=r,r") + (ashift:SI (match_operand:SI 1 "register_operand" "0,0") + (match_operand:QI 2 "nonmemory_operand" "r,K")))] + "" + "ashud\t%2, %0" + [(set_attr "length" "2,2")] +) + +(define_expand "ashr<mode>3" + [(set (match_operand:CR16IM 0 "register_operand" "") + (ashiftrt:CR16IM (match_operand:CR16IM 1 "register_operand" "") + (match_operand:QI 2 "nonmemory_operand" "")))] + "" + { + if (GET_CODE (operands[2]) == CONST_INT) + { + /* If the constant is not in range, try placing it in a reg */ + if (!UNSIGNED_INT_FITS_N_BITS(INTVAL (operands[2]),<shImmBits>)) + operands[2] = copy_to_mode_reg(QImode, operands[2]); + } + + if (GET_CODE (operands[2]) != CONST_INT) + operands[2] = gen_rtx_NEG (QImode, negate_rtx (QImode, operands[2])); + } +) + +(define_insn "ashrqi3_imm_insn" + [(set (match_operand:QI 0 "register_operand" "=c") + (ashiftrt:QI (match_operand:QI 1 "register_operand" "0") + (match_operand:QI 2 "shift_qi_imm_operand" "i")))] + "" + "ashub\t$%n2, %0" + [(set_attr "length" "2")] +) + +(define_insn "ashrhi3_imm_insn" + [(set (match_operand:HI 0 "register_operand" "=c") + (ashiftrt:HI (match_operand:HI 1 "register_operand" "0") + (match_operand:QI 2 "shift_hi_imm_operand" "i")))] + "" + "ashuw\t$%n2, %0" + [(set_attr "length" "2")] +) + +(define_insn "ashrsi3_imm_insn" + [(set (match_operand:SI 0 "register_operand" "=r") + (ashiftrt:SI (match_operand:SI 1 "register_operand" "0") + (match_operand:QI 2 "shift_si_imm_operand" "i")))] + "" + "ashud\t$%n2, %0" + [(set_attr "length" "2")] +) + +(define_insn "ashrqi3_neg_insn" + [(set (match_operand:QI 0 "register_operand" "=c") + (ashiftrt:QI (match_operand:QI 1 "register_operand" "0") + (neg:QI (match_operand:QI 2 "register_operand" "c"))))] + "" + "ashub\t%2,%0" + [(set_attr "length" "2")] +) + +(define_insn "ashrhi3_neg_insn" + [(set (match_operand:HI 0 "register_operand" "=c") + (ashiftrt:HI (match_operand:HI 1 "register_operand" "0") + (neg:QI (match_operand:QI 2 "register_operand" "c"))))] + "" + "ashuw\t%2,%0" + [(set_attr "length" "2")] +) + +(define_insn "ashrdi3_neg_insn" + [(set (match_operand:SI 0 "register_operand" "=r") + (ashiftrt:SI (match_operand:SI 1 "register_operand" "0") + (neg:QI (match_operand:QI 2 "register_operand" "r"))))] + "" + "ashud\t%2,%0" + [(set_attr "length" "2")] +) + +(define_expand "lshr<mode>3" + [(set (match_operand:CR16IM 0 "register_operand" "") + (lshiftrt:CR16IM (match_operand:CR16IM 1 "register_operand" "") + (match_operand:QI 2 "reg_or_int_operand" "")))] + "" + { + if (GET_CODE (operands[2]) == CONST_INT) + { + /* If the constant is not in range, try placing it in a reg */ + if (!UNSIGNED_INT_FITS_N_BITS(INTVAL (operands[2]),<shImmBits>)) + operands[2] = copy_to_mode_reg(QImode, operands[2]); + } + + if (GET_CODE (operands[2]) != CONST_INT) + operands[2] = gen_rtx_NEG (QImode, negate_rtx (QImode, operands[2])); + } +) + +(define_insn "lshrqi3_imm_insn" + [(set (match_operand:QI 0 "register_operand" "=c") + (lshiftrt:QI (match_operand:QI 1 "register_operand" "0") + (match_operand:QI 2 "shift_qi_operand" "Q")))] + "" + "lshb\t$%n2, %0" + [(set_attr "length" "2")] +) + +(define_insn "lshrhi3_imm_insn" + [(set (match_operand:HI 0 "register_operand" "=c") + (lshiftrt:HI (match_operand:HI 1 "register_operand" "0") + (match_operand:QI 2 "shift_hi_operand" "R")))] + "" + "lshw\t$%n2, %0" + [(set_attr "length" "2")] +) + +(define_insn "lshrsi3_imm_insn" + [(set (match_operand:SI 0 "register_operand" "=r") + (lshiftrt:SI (match_operand:SI 1 "register_operand" "0") + (match_operand:QI 2 "shift_si_operand" "S")))] + "" + "lshd\t$%n2, %0" + [(set_attr "length" "2")] +) + +(define_insn "lshrqi3_neg_insn" + [(set (match_operand:QI 0 "register_operand" "=c") + (lshiftrt:QI (match_operand:QI 1 "register_operand" "0") + (neg:QI (match_operand:QI 2 "register_operand" "c"))))] + "" + "lshb\t%2,%0" + [(set_attr "length" "2")] +) + +(define_insn "lshrhi3_neg_insn" + [(set (match_operand:HI 0 "register_operand" "=c") + (lshiftrt:HI (match_operand:HI 1 "register_operand" "0") + (neg:QI (match_operand:QI 2 "register_operand" "c"))))] + "" + "lshw\t%2,%0" + [(set_attr "length" "2")] +) + +(define_insn "lshrsi3_neg_insn" + [(set (match_operand:SI 0 "register_operand" "=r") + (lshiftrt:SI (match_operand:SI 1 "register_operand" "0") + (neg:QI (match_operand:QI 2 "register_operand" "r"))))] + "" + "lshd\t%2,%0" + [(set_attr "length" "2")] +) + +;; Move Instructions + +;; Move any non-immediate operand 0 to a general operand 1. +;; This applies only before starting the reload process +;; Operand 0 is not a register operand of type mode MODE +;; If Operand 0 is a push operand of type mode MODE +;; then, if Operand 1 is a non-SP register +;; then, Operand 1 = copy_to_mode_reg (<MODE>mode, Operand 1) +;; endif +;; else +;; if Operand 1 is either register or 4-bit immediate constant +;; then, Operand 1 = copy_to_mode_reg (<MODE>mode, Operand 1) +;; endif +;; endif +;; +;; What does copy_to_mode_reg (mode, rtx val) do? +;; Copy the value into new temp reg and return the reg where the +;; mode of the new reg is always mode MODE when value is constant +;; +;; Why should copy_to_mode_reg be called? +;; All sorts of move are nor supported by CR16. Therefore, +;; when unsupported move is encountered, the additional instructions +;; will be introduced for the purpose. +;; +;; A new move insn is inserted for Op 1 when one of the following +;; conditions is met. +;; Case 1: Op 0 is push_operand +;; Op 1 is SP register +;; +;; Case 2: Op 0 is not push_operand +;; Op 1 is neither register nor unsigned 4-bit immediate + +(define_expand "mov<mode>" + [(set (match_operand:ALLMTD 0 "nonimmediate_operand" "") + (match_operand:ALLMTD 1 "general_operand" ""))] + "" + { + if (!(reload_in_progress || reload_completed)) + { + /* Only if Op0 is a register operand. */ + if (!register_operand (operands[0], <MODE>mode)) + { + if (push_operand (operands[0], <MODE>mode)) + { + /* Use copy_to_mode_reg only if the register needs + to be pushed is SP as CR16 does not support pushing SP. */ + if (!nosp_reg_operand (operands[1], <MODE>mode)) + operands[1] = copy_to_mode_reg (<MODE>mode, operands[1]); + } + else + { + /* Use copy_to_mode_reg if op1 is not register operand + subject to conditions inside. */ + if (!register_operand (operands[1], <MODE>mode)) + { + /* CR16 does not support moving immediate to SI or SF + type memory. */ + if (<MODE>mode == SImode || <MODE>mode == SFmode || + <MODE>mode == DImode || <MODE>mode == DFmode) + operands[1] = copy_to_mode_reg (<MODE>mode, operands[1]); + else + /* moving imm4 is supported by CR16 instruction. */ + if (!u4bits_operand (operands[1], <MODE>mode)) + operands[1] = copy_to_mode_reg (<MODE>mode, operands[1]); + } + } + } + + /* If operand-1 is a symbol, convert it into a BRO or GOT Format. */ + if (flag_pic && ! legitimate_pic_operand_p (operands[1])) + { + operands[1] = legitimize_pic_address (operands[1], <MODE>mode, 0); + } + } + } +) + +; ALLMT : QI,HI,SI,SF +; pushCnstr : Push constraints +; QI : X +; HI,SI,SF,DI,DF : < +; b : All non-sp registers +; tpush : Push count +; QI,HI : 1 +; SI,SF : 2 +; DI,DF : 4 +(define_insn "push<mode>_internal" + [(set (match_operand:ALLMTD 0 "push_operand" "=<pushCnstr>") + (match_operand:ALLMTD 1 "nosp_reg_operand" "b"))] + "" + "push\t$<tpush>,%p1" + [(set_attr "length" "2")] +) + +; (DI, DF) move +(define_insn "*mov<mode>_double" + [(set (match_operand:DOUBLE 0 "nonimmediate_operand" "=r, r, r, m") + (match_operand:DOUBLE 1 "general_operand" "r, <iFD>, m, r"))] + "register_operand (operands[0], DImode) + || register_operand (operands[0], DFmode) + || register_operand (operands[1], DImode) + || register_operand (operands[1], DFmode)" + { + if (0 == which_alternative) { + rtx xoperands[2] ; + int reg0 = REGNO (operands[0]); + int reg1 = REGNO (operands[1]); + + xoperands[0] = gen_rtx_REG (SImode, reg0 + 2); + xoperands[1] = gen_rtx_REG (SImode, reg1 + 2); + if ((reg1 + 2) != reg0) + { + output_asm_insn ("movd\t%1, %0", operands); + output_asm_insn ("movd\t%1, %0", xoperands); + } + else + { + output_asm_insn ("movd\t%1, %0", xoperands); + output_asm_insn ("movd\t%1, %0", operands); + }} + + else if (1 == which_alternative) { + rtx lo_operands[2] ; + rtx hi_operands[2] ; + + lo_operands[0] = gen_rtx_REG (SImode, REGNO (operands[0])); + hi_operands[0] = gen_rtx_REG (SImode, REGNO (operands[0]) + 2); + lo_operands[1] = simplify_gen_subreg (SImode, operands[1], + VOIDmode == GET_MODE (operands[1]) + ? DImode : GET_MODE (operands[1]), 0); + hi_operands[1] = simplify_gen_subreg (SImode, operands[1], + VOIDmode == GET_MODE (operands[1]) + ? DImode : GET_MODE (operands[1]), 4); + output_asm_insn ("movd\t%1, %0", lo_operands); + output_asm_insn ("movd\t%1, %0", hi_operands);} + + else if (2 == which_alternative) { + rtx xoperands[2] ; + int reg0 = REGNO (operands[0]), reg1 = -2 ; + rtx addr ; + + if (MEM_P (operands[1])) + addr = XEXP (operands[1], 0); + else + addr = NULL_RTX ; + switch (GET_CODE (addr)) + { + case REG: + case SUBREG: + reg1 = REGNO (addr); + break ; + case PLUS: + switch (GET_CODE (XEXP (addr, 0))) { + case REG: + case SUBREG: + reg1 = REGNO (XEXP (addr, 0)); + break ; + case PLUS: + reg1 = REGNO (XEXP (XEXP (addr, 0), 0)); + break ; + default: + inform (DECL_SOURCE_LOCATION (cfun->decl), "unexpected expression; addr:"); + debug_rtx (addr); + inform (DECL_SOURCE_LOCATION (cfun->decl), "operands[1]:"); + debug_rtx (operands[1]); + inform (DECL_SOURCE_LOCATION (cfun->decl), "generated code might now work\n"); + break ;} + break ; + default: + break ; + } + + xoperands[0] = gen_rtx_REG (SImode, reg0 + 2); + xoperands[1] = offset_address (operands[1], GEN_INT (4), 2); + gcc_assert ((reg0 + 1) != reg1); + if (reg0 != reg1 && (reg1 + 1) != reg0) + { + output_asm_insn ("loadd\t%1, %0", operands); + output_asm_insn ("loadd\t%1, %0", xoperands); + } + else + { + output_asm_insn ("loadd\t%1, %0", xoperands); + output_asm_insn ("loadd\t%1, %0", operands); + }} + else + { + rtx xoperands[2] ; + xoperands[0] = offset_address (operands[0], GEN_INT (4), 2); + xoperands[1] = gen_rtx_REG (SImode, REGNO (operands[1]) + 2); + output_asm_insn ("stord\t%1, %0", operands); + output_asm_insn ("stord\t%1, %0", xoperands); + } + return "" ; + } + [(set_attr "length" "4, <lImmArithD>, <lImmArithD>, <lImmArithD>")] +) + +; All long (SI, SF) register move, load and store operations +; The print_operand will take care of printing the register pair +; when mode is SI/SF and register is in SHORT_REGS +(define_insn "*mov<mode>_long" + [(set (match_operand:LONG 0 "nonimmediate_operand" "=r, r, r, m") + (match_operand:LONG 1 "general_operand" "r, <iF>, m, r"))] + "register_operand (operands[0], <MODE>mode) + || register_operand (operands[1], <MODE>mode)" + "@ + mov<tIsa>\t%1, %0 + mov<tIsa>\t%1, %0 + load<tIsa>\t%1, %0 + stor<tIsa>\t%1, %0" + [(set_attr "length" "2,<lImmArith>,<lImmArith>,<lImmArith>")] +) + +;; All short (QI, HI) register move, load and store operations +(define_insn "*mov<mode>_short" + [(set (match_operand:SHORT 0 "nonimmediate_operand" "=r, r, r, m, m") + (match_operand:SHORT 1 "general_operand" "r, <iF>, m, r, <LL>"))] + "(register_operand (operands[0], <MODE>mode)) + || (store_operand (operands[0], <MODE>mode) + && (register_operand (operands[1], <MODE>mode) + || u4bits_operand (operands[1], <MODE>mode)))" + "@ + mov<tIsa>\t%1, %0 + mov<tIsa>\t%1, %0 + load<tIsa>\t%1, %0 + stor<tIsa>\t%1, %0 + stor<tIsa>\t%1, %0" + [(set_attr "length" "2,<lImmArith>,<lImmArith>,<lImmArith>,<lImmArith>")] +) + +;; Compare Instructions +; Instruction generated compares the operands in reverse order +; Therefore, while printing the asm, the reverse of the +; compare condition shall be printed. +(define_insn "cbranch<mode>4" + [(set (pc) + (if_then_else (match_operator 0 "ordered_comparison_operator" + [(match_operand:CR16IM 1 "register_operand" "r,r") + (match_operand:CR16IM 2 "nonmemory_operand" "r,n")]) + (label_ref (match_operand 3 "" "")) + (pc))) + (clobber (cc0))] + "" + "cmp<tIsa>\t%2, %1\;b%d0\t%l3" + [(set_attr "length" "6,6")] +) + +(define_expand "cmp<mode>" + [(parallel [(set (cc0) + (compare (match_operand:CR16IM 0 "register_operand" "") + (match_operand:CR16IM 1 "nonmemory_operand" ""))) + (clobber (match_scratch:HI 2 "=r"))] ) ] + "" + "") + +;; Scond Instructions +(define_expand "cstore<mode>4" + [(set (cc0) + (compare (match_operand:CR16IM 2 "register_operand" "") + (match_operand:CR16IM 3 "nonmemory_operand" ""))) + (set (match_operand:HI 0 "register_operand") + (match_operator:HI 1 "ordered_comparison_operator" + [(cc0) (const_int 0)]))] + "" + "" +) + +(define_insn "*cmp<mode>_insn" + [(set (cc0) + (compare (match_operand:CR16IM 0 "register_operand" "r,r") + (match_operand:CR16IM 1 "nonmemory_operand" "r,n")))] + "" + "cmp<tIsa>\t%1, %0" + [(set_attr "length" "2,4")] +) + +(define_insn "sCOND_internal" + [(set (match_operand:HI 0 "register_operand" "=r") + (match_operator:HI 1 "ordered_comparison_operator" + [(cc0) (const_int 0)]))] + "" + "s%d1\t%0" + [(set_attr "length" "2")] +) + +;; Jumps and Branches +(define_insn "indirect_jump_return" + [(set (pc) + (reg:SI RA_REGNUM)) + (return)] + "reload_completed" + "jump\t (ra)" + [(set_attr "length" "2")] +) + +(define_insn "jump_return" + [(unspec:SI [(const_int 0)] UNSPEC_RETURN_ADDR) + (return)] + "reload_completed" + "jump\t(ra)" + [(set_attr "length" "2")] +) + +(define_insn "indirect_jump" + [(set (pc) + (match_operand:SI 0 "reg_or_sym_operand" "r,i"))] + "" + "@ + jump\t%0 + br\t%a0" + [(set_attr "length" "2,6")] +) + +(define_insn "interrupt_return" + [(unspec_volatile [(const_int 0)] 0) + (return)] + "" + { + return cr16_prepare_push_pop_string (1); + } + [(set_attr "length" "14")] +) + +(define_insn "jump_to_imm" + [(set (pc) + (match_operand 0 "jump_imm_operand" "i"))] + "" + "br\t%c0" + [(set_attr "length" "6")] +) + +(define_insn "jump" + [(set (pc) + (label_ref (match_operand 0 "" "")))] + "" + "br\t%l0" + [(set_attr "length" "6")] +) + +;; Table Jump +(define_insn "tablejump" + [(set (pc) + (match_operand:SI 0 "register_operand" "r")) + (use (label_ref:SI (match_operand 1 "" "")))] + "!flag_pic" + "jump\t%0" + [(set_attr "length" "2")] +) + +;; Call Instructions +(define_expand "call" + [(call (match_operand:QI 0 "memory_operand" "") + (match_operand 1 "" ""))] + "" + { + if (flag_pic && ! legitimate_pic_operand_p (operands[0])) + { + operands[0] = gen_const_mem (QImode, + legitimize_pic_address (XEXP (operands[0], 0), Pmode, 0)); + emit_call_insn (gen_cr16_call (operands[0], operands[1])); + } + else + emit_call_insn (gen_cr16_call (operands[0], operands[1])); + DONE; + } +) + +(define_expand "cr16_call" + [(parallel + [(call (match_operand:QI 0 "memory_operand" "") + (match_operand 1 "" "")) + (clobber (reg:SI RA_REGNUM))])] + "" + "" +) + +(define_insn "cr16_call_insn_branch_pic" + [(call (mem:QI (match_operand:SI 0 "call_imm_operand" "i")) + (match_operand 1 "" "")) + (clobber (match_operand:SI 2 "register_operand" "+r"))] + "flag_pic == FAR_PIC" + { + if (GET_CODE (operands[0]) != CONST_INT) + return "loadd\t%g0, %2 \n\tjal %2"; + else + return "jal %2"; + } + [(set_attr "length" "8")] +) + +(define_insn "cr16_call_insn_branch" + [(call (mem:QI (match_operand:SI 0 "call_imm_operand" "i")) + (match_operand 1 "" "")) + (clobber (match_operand:SI 2 "register_operand" "+r"))] + "flag_pic == 0 || flag_pic == NEAR_PIC" + { + /* Print the immediate address for bal + 'b' is used instead of 'a' to avoid compiler calling + the GO_IF_LEGITIMATE_ADDRESS which cannot + perform checks on const_int code addresses as it + assumes all const_int are data addresses. + */ + if (GET_CODE (operands[0]) != CONST_INT) + return "bal (ra), %a0"; + else + operands[4] = GEN_INT ((INTVAL (operands[0]))>>1); + return "movd\t%g4,\t(r1,r0)\n\tjal\t(r1,r0)"; + } + [(set_attr "length" "6")] +) + +(define_insn "cr16_call_insn_jump" + [(call (mem:QI (match_operand:SI 0 "register_operand" "r")) + (match_operand 1 "" "")) + (clobber (match_operand:SI 2 "register_operand" "+r"))] + "" + "jal\t%0" + [(set_attr "length" "2")] +) + +;; Call Value Instructions + +(define_expand "call_value" + [(set (match_operand 0 "general_operand" "") + (call (match_operand:QI 1 "memory_operand" "") + (match_operand 2 "" "")))] + "" + { + if (flag_pic && !legitimate_pic_operand_p (operands[1])) + { + operands[1] = gen_const_mem (QImode, + legitimize_pic_address (XEXP (operands[1], 0), Pmode, 0)); + emit_call_insn (gen_cr16_call_value (operands[0], operands[1], operands[2])); + } + else + emit_call_insn (gen_cr16_call_value (operands[0], operands[1], operands[2])); + DONE; + } +) + +(define_expand "cr16_call_value" + [(parallel + [(set (match_operand 0 "general_operand" "") + (call (match_operand 1 "memory_operand" "") + (match_operand 2 "" ""))) + (clobber (reg:SI RA_REGNUM))])] + "" + "" +) + +(define_insn "cr16_call_value_insn_branch_pic" + [(set (match_operand 0 "" "=g") + (call (mem:QI (match_operand:SI 1 "call_imm_operand" "i")) + (match_operand 2 "" ""))) + (clobber (match_operand:SI 3 "register_operand" "+r"))] + "flag_pic == FAR_PIC" + { + if (GET_CODE (operands[1]) != CONST_INT) + return "loadd\t%g1, %3 \n\tjal %3"; + else + return "jal %3"; + } + [(set_attr "length" "8")] +) + +(define_insn "cr16_call_value_insn_branch" + [(set (match_operand 0 "" "=g") + (call (mem:QI (match_operand:SI 1 "call_imm_operand" "i")) + (match_operand 2 "" ""))) + (clobber (match_operand:SI 3 "register_operand" "+r"))] + "flag_pic == 0 || flag_pic == NEAR_PIC" + { + /* Print the immediate address for bal + 'b' is used instead of 'a' to avoid compiler calling + the GO_IF_LEGITIMATE_ADDRESS which cannot + perform checks on const_int code addresses as it + assumes all const_int are data addresses. + */ + if (GET_CODE (operands[1]) != CONST_INT) + return "bal (ra), %a1"; + else + { + operands[4] = GEN_INT ((INTVAL (operands[1]))>>1); + return "movd\t%g4,\t(r1,r0)\n\tjal\t(r1,r0)"; + } + } + [(set_attr "length" "6")] +) + + +(define_insn "cr16_call_value_insn_jump" + [(set (match_operand 0 "" "=g") + (call (mem:QI (match_operand:SI 1 "register_operand" "r")) + (match_operand 2 "" ""))) + (clobber (match_operand:SI 3 "register_operand" "+r"))] + "" + "jal\t%1" + [(set_attr "length" "2")] +) + + +;; Nop +(define_insn "nop" + [(const_int 0)] + "" + "nop\t" +) + +;; PIC +/* When generating pic, we need to load the symbol offset into a register. + So that the optimizer does not confuse this with a normal symbol load + we use an unspec. The offset will be loaded from a constant pool entry, + since that is the only type of relocation we can use. */ + +(define_insn "unspec_bro_addr" + [(set (match_operand:SI 0 "register_operand" "=r") + (unspec:SI [(match_operand 1 "" "")] UNSPEC_PIC_ADDR))] + "" + "movd \t%f1, %0" + [(set_attr "length" "4")] +) + +(define_insn "unspec_got_addr" + [(set (match_operand:SI 0 "register_operand" "=r") + (unspec:SI [(match_operand 1 "" "")] UNSPEC_PIC_LOAD_ADDR))] + "" + "loadd \t%g1, %0" + [(set_attr "length" "6")] +) diff --git a/gcc/config/cr16/cr16.opt b/gcc/config/cr16/cr16.opt new file mode 100644 index 00000000000..a006b01523a --- /dev/null +++ b/gcc/config/cr16/cr16.opt @@ -0,0 +1,51 @@ +; Options for the National Semiconductor CR16 port of the compiler. +; Copyright (C) 2012 Free Software Foundation, Inc. +; Contributed by KPIT Cummins Infosystems Limited. +; +; This file is part of GCC. +; +; GCC is free software; you can redistribute it and/or modify it +; under the terms of the GNU General Public License as published +; by the Free Software Foundation; either version 3, or (at your +; option) any later version. +; +; GCC is distributed in the hope that it will be useful, but WITHOUT +; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +; License for more details. +; +; You should have received a copy of the GNU General Public License +; along with GCC; see the file COPYING3. If not see +; <http://www.gnu.org/licenses/>. + +msim +Target +-msim Use simulator runtime + +mbit-ops +Target Report Mask(BIT_OPS) +Generate SBIT, CBIT instructions + +mmac +Target Report Mask(MAC) +Support multiply accumulate instructions + +mdebug-addr +Target RejectNegative Var(TARGET_DEBUG_ADDR) Undocumented + +mdata-model= +Target RejectNegative JoinedOrMissing Var(cr16_data_model) +Treat data references as near, far or medium. medium is default + +mcr16c +Target RejectNegative Mask(CR16C) +Generate code for CR16C architecture + +mcr16cplus +Target RejectNegative InverseMask(CR16C,CR16CP) +Generate code for CR16C+ architecture (Default) + +mint32 +Target RejectNegative Mask(INT32) +Treat integers as 32-bit. + diff --git a/gcc/config/cr16/predicates.md b/gcc/config/cr16/predicates.md new file mode 100644 index 00000000000..d399c339347 --- /dev/null +++ b/gcc/config/cr16/predicates.md @@ -0,0 +1,225 @@ +;; Predicates of machine description for CR16. +;; Copyright (C) 2012 Free Software Foundation, Inc. +;; Contributed by KPIT Cummins Infosystems Limited. +;; +;; This file is part of GCC. +;; +;; GCC is free software; you can redistribute it and/or modify it +;; under the terms of the GNU General Public License as published +;; by the Free Software Foundation; either version 3, or (at your +;; option) any later version. +;; +;; GCC is distributed in the hope that it will be useful, but WITHOUT +;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +;; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +;; License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with GCC; see the file COPYING3. If not see +;; <http://www.gnu.org/licenses/>. + +;; Predicates + +;; Predicates for sbit/cbit instructions +;; bit operand used for the generation of bit insn generation +(define_predicate "bit_operand" + (match_code "mem") +{ + return ((GET_CODE (op) == MEM && OK_FOR_Z (op))); +}) + +;; Unsigned 4-bits constant int or double value. +(define_predicate "u4bits_operand" + (match_code "const_int,const_double") +{ + if (GET_CODE (op) == CONST_DOUBLE) + return cr16_const_double_ok (op); + return (UNSIGNED_INT_FITS_N_BITS(INTVAL (op), 4)) ? 1 : 0; +}) + +;; Operand is a constant integer where +;; only one bit is set to 1. +(define_predicate "one_bit_operand" + (match_code "const_int") +{ + unsigned int val; + + val = INTVAL (op); + if (mode == QImode) + val &= 0xff; + else if (mode == HImode) + val &= 0xffff; + else + gcc_unreachable(); + + if (val != 0) + return (val & (val - 1)) == 0; /* true if only one bit is set. */ + else + return 0; +}) + +;; Operand is a constant integer where +;; only one bit is set to 0. +(define_predicate "rev_one_bit_operand" + (match_code "const_int") +{ + unsigned int val; + + val = ~INTVAL (op); /* Invert and use. */ + if (mode == QImode) + val &= 0xff; + else if (mode == HImode) + val &= 0xffff; + else + gcc_unreachable(); + + if (val != 0) + return (val & (val - 1)) == 0; /* true if only one bit is set. */ + else + return 0; +}) + +;; Predicates for shift instructions +;; Immediate operand predicate for count in shift operations. +;; Immediate shall be 3-bits in case operand to be operated on +;; is a qi mode operand. +(define_predicate "shift_qi_imm_operand" + (match_code "const_int") +{ + return (UNSIGNED_INT_FITS_N_BITS(INTVAL (op), 3)) ? 1 : 0; +}) + +;; Immediate shall be 4-bits in case operand to be operated on +;; is a hi mode operand. +(define_predicate "shift_hi_imm_operand" + (match_code "const_int") +{ + return (UNSIGNED_INT_FITS_N_BITS(INTVAL (op), 4)) ? 1 : 0; +}) + +;; Immediate shall be 3-bits in case operand to be operated on +;; is a si mode operand. +(define_predicate "shift_si_imm_operand" + (match_code "const_int") +{ + return (UNSIGNED_INT_FITS_N_BITS(INTVAL (op), 5)) ? 1 : 0; +}) + +;; Predicates for jump/call instructions +;; Jump immediate cannot be more than 24-bits +(define_predicate "jump_imm_operand" + (match_code "const_int") +{ + return (UNSIGNED_INT_FITS_N_BITS(INTVAL (op), 24)) ? 1 : 0; +}) + +;; Call immediate cannot be more than 24-bits +(define_predicate "call_imm_operand" + (match_operand 0 "immediate_operand") +{ + if (GET_CODE (op) != CONST_INT) return 1; + return (UNSIGNED_INT_FITS_N_BITS(INTVAL (op), 24)) ? 1 : 0; +}) + +;; Operand is register or 4-bit immediate operand +(define_predicate "reg_or_u4bits_operand" + (ior (match_operand 0 "u4bits_operand") + (match_operand 0 "register_operand"))) + +;; Operand is a register or symbol reference +(define_predicate "reg_or_sym_operand" + (ior (match_code "symbol_ref") + (match_operand 0 "register_operand"))) + +;; Operand is a non stack pointer register +(define_predicate "nosp_reg_operand" + (and (match_operand 0 "register_operand") + (match_test "REGNO (op) != SP_REGNUM"))) + +(define_predicate "hard_reg_operand" + (and (match_operand 0 "register_operand") + (match_test "REGNO (op) <= 15"))) + +;; Operand is a memory reference and +;; not a push operand. +(define_predicate "store_operand" + (and (match_operand 0 "memory_operand") + (not (match_operand 0 "push_operand")))) + +;; Helper predicate +(define_predicate "reg_or_int_operand" + (ior (match_code "const_int") + (match_operand 0 "register_operand"))) + +;; +;; +;; Atithmetic/logical predicates + +;; QI Helper +(define_predicate "arith_qi_operand" + (match_code "const_int") +{ + return (IN_RAN(INTVAL (op), 0, 15) && ((INTVAL (op) != 9) + || (INTVAL (op) != 11))) ? 1 : 0 ; +}) + +;;QI Reg, subreg(reg) or const_int. +(define_predicate "reg_qi_int_operand" + (ior (match_operand 0 "arith_qi_operand") + (match_operand 0 "register_operand"))) + +;; HI Helper +(define_predicate "arith_hi_operand" + (match_code "const_int") +{ + return (IN_RAN(INTVAL (op), -32768, 32768) ) ? 1 : 0 ; +}) + +;;HI Reg, subreg(reg) or const_int. +(define_predicate "reg_hi_int_operand" + (ior (match_operand 0 "arith_hi_operand") + (match_operand 0 "register_operand"))) + +;;SI Reg, subreg(reg) or const_int. +(define_predicate "reg_si_int_operand" + (ior (match_operand 0 "const_int_operand") + (match_operand 0 "register_operand"))) + +;; +;; Shift predicates + +;; QI Helper +(define_predicate "shift_qi_operand" + (match_code "const_int") +{ + return (IN_RAN(INTVAL (op), 0, 7) ) ? 1 : 0; +}) + +;;QI Reg, subreg(reg) or const_int. +(define_predicate "shift_reg_qi_int_operand" + (ior (match_operand 0 "shift_qi_operand") + (match_operand 0 "register_operand"))) + +;; HI Helper +(define_predicate "shift_hi_operand" + (match_code "const_int") +{ + return (IN_RAN(INTVAL (op), 0, 15) ) ? 1 : 0 ; +}) + +;;HI Reg, subreg(reg) or const_int. +(define_predicate "shift_reg_hi_int_operand" + (ior (match_operand 0 "shift_hi_operand") + (match_operand 0 "register_operand"))) + +;; SI Helper +(define_predicate "shift_si_operand" + (match_code "const_int") +{ + return (IN_RAN(INTVAL (op), 0, 31) ) ? 1 : 0; +}) + +;;SI Reg, subreg(reg) or const_int. +(define_predicate "shift_reg_si_int_operand" + (ior (match_operand 0 "shift_si_operand") + (match_operand 0 "register_operand"))) diff --git a/gcc/config/cr16/t-cr16 b/gcc/config/cr16/t-cr16 new file mode 100644 index 00000000000..ff9b677b19f --- /dev/null +++ b/gcc/config/cr16/t-cr16 @@ -0,0 +1,25 @@ +# CR16 Target Makefile +# Copyright (C) 2012 Free Software Foundation, Inc. +# Contributed by KPIT Cummins Infosystems Limited. +# +# This file is part of GCC. +# +# GCC is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published +# by the Free Software Foundation; either version 3, or (at your +# option) any later version. +# +# GCC is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +# License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GCC; see the file COPYING3. If not see +# <http://www.gnu.org/licenses/>. + +MULTILIB_OPTIONS = fPIC mint32 +MULTILIB_DIRNAMES = far-pic int32 +MULTILIB_MATCHES = +MULTILIB_EXTRA_OPTS = mcr16cplus mdata-model=far + |