OpenCores
URL https://opencores.org/ocsvn/openrisc/openrisc/trunk

Subversion Repositories openrisc

[/] [openrisc/] [trunk/] [gnu-dev/] [or1k-gcc/] [gcc/] [config/] [cr16/] [cr16.c] - Rev 709

Compare with Previous | Blame | View Log

/* 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;
 

Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.