summaryrefslogtreecommitdiff
path: root/gcc/config/cr16
diff options
context:
space:
mode:
authorjayants <jayants@138bc75d-0d04-0410-961f-82ee72b054a4>2012-02-02 10:20:32 +0000
committerjayants <jayants@138bc75d-0d04-0410-961f-82ee72b054a4>2012-02-02 10:20:32 +0000
commitb9fdd12b8db041ca3d5d0bc23491fdc6db86f484 (patch)
tree6c6a242bece77442e5ca1c66403d5cee85d5584f /gcc/config/cr16
parent80b67e55d6cbd8abbe5dbb0087eeea0dc3c27941 (diff)
downloadgcc-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.md81
-rw-r--r--gcc/config/cr16/cr16-protos.h101
-rw-r--r--gcc/config/cr16/cr16.c2190
-rw-r--r--gcc/config/cr16/cr16.h590
-rw-r--r--gcc/config/cr16/cr16.md1084
-rw-r--r--gcc/config/cr16/cr16.opt51
-rw-r--r--gcc/config/cr16/predicates.md225
-rw-r--r--gcc/config/cr16/t-cr1625
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
+