diff options
author | Sumanth G <sumanth.gundapaneni@kpitcummins.com> | 2012-02-02 10:20:32 +0000 |
---|---|---|
committer | Jayant Sonar <jayants@gcc.gnu.org> | 2012-02-02 10:20:32 +0000 |
commit | b25364a0b9e59bb9ef6d7d9740dfb750ec27f40d (patch) | |
tree | 6c6a242bece77442e5ca1c66403d5cee85d5584f /gcc/config/cr16/cr16.c | |
parent | 903e49f52065e9547a6e534d1bedbc980f77a5b1 (diff) | |
download | gcc-b25364a0b9e59bb9ef6d7d9740dfb750ec27f40d.zip gcc-b25364a0b9e59bb9ef6d7d9740dfb750ec27f40d.tar.gz gcc-b25364a0b9e59bb9ef6d7d9740dfb750ec27f40d.tar.bz2 |
Adding GCC port for National Semicondutor's CR16 architecture
Co-Authored-By: Jayant R Sonar <jayant.sonar@kpitcummins.com>
From-SVN: r183832
Diffstat (limited to 'gcc/config/cr16/cr16.c')
-rw-r--r-- | gcc/config/cr16/cr16.c | 2190 |
1 files changed, 2190 insertions, 0 deletions
diff --git a/gcc/config/cr16/cr16.c b/gcc/config/cr16/cr16.c new file mode 100644 index 0000000..65968f8 --- /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; |