view gcc/config/mep/mep.c @ 14:f6334be47118

update gcc from gcc-4.6-20100522 to gcc-4.6-20110318
author nobuyasu <dimolto@cr.ie.u-ryukyu.ac.jp>
date Tue, 22 Mar 2011 17:18:12 +0900
parents b7f97abdc517
children
line wrap: on
line source

/* Definitions for Toshiba Media Processor
   Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
   Free Software Foundation, Inc.
   Contributed by Red Hat, Inc.

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 "regs.h"
#include "hard-reg-set.h"
#include "insn-config.h"
#include "conditions.h"
#include "insn-flags.h"
#include "output.h"
#include "insn-attr.h"
#include "flags.h"
#include "recog.h"
#include "obstack.h"
#include "tree.h"
#include "expr.h"
#include "except.h"
#include "function.h"
#include "optabs.h"
#include "reload.h"
#include "tm_p.h"
#include "ggc.h"
#include "diagnostic-core.h"
#include "integrate.h"
#include "target.h"
#include "target-def.h"
#include "langhooks.h"
#include "df.h"
#include "gimple.h"

/* Structure of this file:

 + Command Line Option Support
 + Pattern support - constraints, predicates, expanders
 + Reload Support
 + Costs
 + Functions to save and restore machine-specific function data.
 + Frame/Epilog/Prolog Related
 + Operand Printing
 + Function args in registers
 + Handle pipeline hazards
 + Handle attributes
 + Trampolines
 + Machine-dependent Reorg
 + Builtins.  */

/* Symbol encodings:

   Symbols are encoded as @ <char> . <name> where <char> is one of these:

   b - based
   t - tiny
   n - near
   f - far
   i - io, near
   I - io, far
   c - cb (control bus)  */

struct GTY(()) machine_function
{
  int mep_frame_pointer_needed;
  
  /* For varargs. */
  int arg_regs_to_save;
  int regsave_filler;
  int frame_filler;
  int frame_locked;
  
  /* Records __builtin_return address.  */
  rtx eh_stack_adjust;
  
  int reg_save_size;
  int reg_save_slot[FIRST_PSEUDO_REGISTER];
  unsigned char reg_saved[FIRST_PSEUDO_REGISTER];
  
  /* 2 if the current function has an interrupt attribute, 1 if not, 0
     if unknown.  This is here because resource.c uses EPILOGUE_USES
     which needs it.  */
  int interrupt_handler;
  
  /* Likewise, for disinterrupt attribute.  */
  int disable_interrupts;

  /* Number of doloop tags used so far.  */
  int doloop_tags;

  /* True if the last tag was allocated to a doloop_end.  */
  bool doloop_tag_from_end;

  /* True if reload changes $TP.  */
  bool reload_changes_tp;

  /* 2 if there are asm()s without operands, 1 if not, 0 if unknown.
     We only set this if the function is an interrupt handler.  */
  int asms_without_operands;
};

#define MEP_CONTROL_REG(x) \
  (GET_CODE (x) == REG && ANY_CONTROL_REGNO_P (REGNO (x)))

static GTY(()) section * based_section;
static GTY(()) section * tinybss_section;
static GTY(()) section * far_section;
static GTY(()) section * farbss_section;
static GTY(()) section * frodata_section;
static GTY(()) section * srodata_section;

static GTY(()) section * vtext_section;
static GTY(()) section * vftext_section;
static GTY(()) section * ftext_section;

static void mep_set_leaf_registers (int);
static bool symbol_p (rtx);
static bool symbolref_p (rtx);
static void encode_pattern_1 (rtx);
static void encode_pattern (rtx);
static bool const_in_range (rtx, int, int);
static void mep_rewrite_mult (rtx, rtx);
static void mep_rewrite_mulsi3 (rtx, rtx, rtx, rtx);
static void mep_rewrite_maddsi3 (rtx, rtx, rtx, rtx, rtx);
static bool mep_reuse_lo_p_1 (rtx, rtx, rtx, bool);
static bool move_needs_splitting (rtx, rtx, enum machine_mode);
static bool mep_expand_setcc_1 (enum rtx_code, rtx, rtx, rtx);
static bool mep_nongeneral_reg (rtx);
static bool mep_general_copro_reg (rtx);
static bool mep_nonregister (rtx);
static struct machine_function* mep_init_machine_status (void);
static rtx mep_tp_rtx (void);
static rtx mep_gp_rtx (void);
static bool mep_interrupt_p (void);
static bool mep_disinterrupt_p (void);
static bool mep_reg_set_p (rtx, rtx);
static bool mep_reg_set_in_function (int);
static bool mep_interrupt_saved_reg (int);
static bool mep_call_saves_register (int);
static rtx F (rtx);
static void add_constant (int, int, int, int);
static rtx maybe_dead_move (rtx, rtx, bool);
static void mep_reload_pointer (int, const char *);
static void mep_start_function (FILE *, HOST_WIDE_INT);
static bool mep_function_ok_for_sibcall (tree, tree);
static int unique_bit_in (HOST_WIDE_INT);
static int bit_size_for_clip (HOST_WIDE_INT);
static int bytesize (const_tree, enum machine_mode);
static tree mep_validate_based_tiny (tree *, tree, tree, int, bool *);
static tree mep_validate_near_far (tree *, tree, tree, int, bool *);
static tree mep_validate_disinterrupt (tree *, tree, tree, int, bool *);
static tree mep_validate_interrupt (tree *, tree, tree, int, bool *);
static tree mep_validate_io_cb (tree *, tree, tree, int, bool *);
static tree mep_validate_vliw (tree *, tree, tree, int, bool *);
static bool mep_function_attribute_inlinable_p (const_tree);
static bool mep_can_inline_p (tree, tree);
static bool mep_lookup_pragma_disinterrupt (const char *);
static int mep_multiple_address_regions (tree, bool);
static int mep_attrlist_to_encoding (tree, tree);
static void mep_insert_attributes (tree, tree *);
static void mep_encode_section_info (tree, rtx, int);
static section * mep_select_section (tree, int, unsigned HOST_WIDE_INT);
static void mep_unique_section (tree, int);
static unsigned int mep_section_type_flags (tree, const char *, int);
static void mep_asm_named_section (const char *, unsigned int, tree);
static bool mep_mentioned_p (rtx, rtx, int);
static void mep_reorg_regmove (rtx);
static rtx mep_insert_repeat_label_last (rtx, rtx, bool, bool);
static void mep_reorg_repeat (rtx);
static bool mep_invertable_branch_p (rtx);
static void mep_invert_branch (rtx, rtx);
static void mep_reorg_erepeat (rtx);
static void mep_jmp_return_reorg (rtx);
static void mep_reorg_addcombine (rtx);
static void mep_reorg (void);
static void mep_init_intrinsics (void);
static void mep_init_builtins (void);
static void mep_intrinsic_unavailable (int);
static bool mep_get_intrinsic_insn (int, const struct cgen_insn **);
static bool mep_get_move_insn (int, const struct cgen_insn **);
static rtx mep_convert_arg (enum machine_mode, rtx);
static rtx mep_convert_regnum (const struct cgen_regnum_operand *, rtx);
static rtx mep_legitimize_arg (const struct insn_operand_data *, rtx, int);
static void mep_incompatible_arg (const struct insn_operand_data *, rtx, int, tree);
static rtx mep_expand_builtin (tree, rtx, rtx, enum machine_mode, int);
static int mep_adjust_cost (rtx, rtx, rtx, int);
static int mep_issue_rate (void);
static rtx mep_find_ready_insn (rtx *, int, enum attr_slot, int);
static void mep_move_ready_insn (rtx *, int, rtx);
static int mep_sched_reorder (FILE *, int, rtx *, int *, int);
static rtx mep_make_bundle (rtx, rtx);
static void mep_bundle_insns (rtx);
static bool mep_rtx_cost (rtx, int, int, int *, bool);
static int mep_address_cost (rtx, bool);
static void mep_setup_incoming_varargs (CUMULATIVE_ARGS *, enum machine_mode,
					tree, int *, int);
static bool mep_pass_by_reference (CUMULATIVE_ARGS * cum, enum machine_mode,
				   const_tree, bool);
static rtx mep_function_arg (CUMULATIVE_ARGS *, enum machine_mode,
			     const_tree, bool);
static void mep_function_arg_advance (CUMULATIVE_ARGS *, enum machine_mode,
				      const_tree, bool);
static bool mep_vector_mode_supported_p (enum machine_mode);
static bool mep_handle_option (size_t, const char *, int);
static rtx  mep_allocate_initial_value (rtx);
static void mep_asm_init_sections (void);
static int mep_comp_type_attributes (const_tree, const_tree);
static bool mep_narrow_volatile_bitfield (void);
static rtx mep_expand_builtin_saveregs (void);
static tree mep_build_builtin_va_list (void);
static void mep_expand_va_start (tree, rtx);
static tree mep_gimplify_va_arg_expr (tree, tree, gimple_seq *, gimple_seq *);
static bool mep_can_eliminate (const int, const int);
static void mep_conditional_register_usage (void);
static void mep_trampoline_init (rtx, tree, rtx);

#define WANT_GCC_DEFINITIONS
#include "mep-intrin.h"
#undef WANT_GCC_DEFINITIONS


/* Command Line Option Support.  */

char mep_leaf_registers [FIRST_PSEUDO_REGISTER];

/* True if we can use cmov instructions to move values back and forth
   between core and coprocessor registers.  */
bool mep_have_core_copro_moves_p;

/* True if we can use cmov instructions (or a work-alike) to move
   values between coprocessor registers.  */
bool mep_have_copro_copro_moves_p;

/* A table of all coprocessor instructions that can act like
   a coprocessor-to-coprocessor cmov.  */
static const int mep_cmov_insns[] = {
  mep_cmov,
  mep_cpmov,
  mep_fmovs,
  mep_caddi3,
  mep_csubi3,
  mep_candi3,
  mep_cori3,
  mep_cxori3,
  mep_cand3,
  mep_cor3
};

static int option_mtiny_specified = 0;


static void
mep_set_leaf_registers (int enable)
{
  int i;

  if (mep_leaf_registers[0] != enable)
    for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
      mep_leaf_registers[i] = enable;
}

static void
mep_conditional_register_usage (void)
{
  int i;

  if (!TARGET_OPT_MULT && !TARGET_OPT_DIV)
    {
      fixed_regs[HI_REGNO] = 1;
      fixed_regs[LO_REGNO] = 1;
      call_used_regs[HI_REGNO] = 1;
      call_used_regs[LO_REGNO] = 1;
    }

  for (i = FIRST_SHADOW_REGISTER; i <= LAST_SHADOW_REGISTER; i++)
    global_regs[i] = 1;
}


static const struct default_options mep_option_optimization_table[] =
  {
    /* The first scheduling pass often increases register pressure and
       tends to result in more spill code.  Only run it when
       specifically asked.  */
    { OPT_LEVELS_ALL, OPT_fschedule_insns, NULL, 0 },

    /* Using $fp doesn't gain us much, even when debugging is
       important.  */
    { OPT_LEVELS_ALL, OPT_fomit_frame_pointer, NULL, 1 },

    { OPT_LEVELS_NONE, 0, NULL, 0 }
  };

static void
mep_option_override (void)
{
  if (flag_pic == 1)
    warning (OPT_fpic, "-fpic is not supported");
  if (flag_pic == 2)
    warning (OPT_fPIC, "-fPIC is not supported");
  if (TARGET_S && TARGET_M)
    error ("only one of -ms and -mm may be given");
  if (TARGET_S && TARGET_L)
    error ("only one of -ms and -ml may be given");
  if (TARGET_M && TARGET_L)
    error ("only one of -mm and -ml may be given");
  if (TARGET_S && option_mtiny_specified)
    error ("only one of -ms and -mtiny= may be given");
  if (TARGET_M && option_mtiny_specified)
    error ("only one of -mm and -mtiny= may be given");
  if (TARGET_OPT_CLIP && ! TARGET_OPT_MINMAX)
    warning (0, "-mclip currently has no effect without -mminmax");

  if (mep_const_section)
    {
      if (strcmp (mep_const_section, "tiny") != 0
	  && strcmp (mep_const_section, "near") != 0
	  && strcmp (mep_const_section, "far") != 0)
	error ("-mc= must be -mc=tiny, -mc=near, or -mc=far");
    }

  if (TARGET_S)
    mep_tiny_cutoff = 65536;
  if (TARGET_M)
    mep_tiny_cutoff = 0;
  if (TARGET_L && ! option_mtiny_specified)
    mep_tiny_cutoff = 0;

  if (TARGET_64BIT_CR_REGS)
    flag_split_wide_types = 0;

  init_machine_status = mep_init_machine_status;
  mep_init_intrinsics ();
}
  
/* Pattern Support - constraints, predicates, expanders.  */

/* MEP has very few instructions that can refer to the span of
   addresses used by symbols, so it's common to check for them.  */

static bool
symbol_p (rtx x)
{
  int c = GET_CODE (x);

  return (c == CONST_INT
	  || c == CONST
	  || c == SYMBOL_REF);
}

static bool
symbolref_p (rtx x)
{
  int c;

  if (GET_CODE (x) != MEM)
    return false;

  c = GET_CODE (XEXP (x, 0));
  return (c == CONST_INT
	  || c == CONST
	  || c == SYMBOL_REF);
}

/* static const char *reg_class_names[] = REG_CLASS_NAMES; */

#define GEN_REG(R, STRICT)				\
  (GR_REGNO_P (R)					\
   || (!STRICT						\
       && ((R) == ARG_POINTER_REGNUM			\
	   || (R) >= FIRST_PSEUDO_REGISTER)))

static char pattern[12], *patternp;
static GTY(()) rtx patternr[12];
#define RTX_IS(x) (strcmp (pattern, x) == 0)

static void
encode_pattern_1 (rtx x)
{
  int i;

  if (patternp == pattern + sizeof (pattern) - 2)
    {
      patternp[-1] = '?';
      return;
    }

  patternr[patternp-pattern] = x;

  switch (GET_CODE (x))
    {
    case REG:
      *patternp++ = 'r';
      break;
    case MEM:
      *patternp++ = 'm';
    case CONST:
      encode_pattern_1 (XEXP(x, 0));
      break;
    case PLUS:
      *patternp++ = '+';
      encode_pattern_1 (XEXP(x, 0));
      encode_pattern_1 (XEXP(x, 1));
      break;
    case LO_SUM:
      *patternp++ = 'L';
      encode_pattern_1 (XEXP(x, 0));
      encode_pattern_1 (XEXP(x, 1));
      break;
    case HIGH:
      *patternp++ = 'H';
      encode_pattern_1 (XEXP(x, 0));
      break;
    case SYMBOL_REF:
      *patternp++ = 's';
      break;
    case LABEL_REF:
      *patternp++ = 'l';
      break;
    case CONST_INT:
    case CONST_DOUBLE:
      *patternp++ = 'i';
      break;
    case UNSPEC:
      *patternp++ = 'u';
      *patternp++ = '0' + XCINT(x, 1, UNSPEC);
      for (i=0; i<XVECLEN (x, 0); i++)
	encode_pattern_1 (XVECEXP (x, 0, i));
      break;
    case USE:
      *patternp++ = 'U';
      break;
    default:
      *patternp++ = '?';
#if 0
      fprintf (stderr, "can't encode pattern %s\n", GET_RTX_NAME(GET_CODE(x)));
      debug_rtx (x);
      gcc_unreachable ();
#endif
      break;
    }      
}

static void
encode_pattern (rtx x)
{
  patternp = pattern;
  encode_pattern_1 (x);
  *patternp = 0;
}

int
mep_section_tag (rtx x)
{
  const char *name;

  while (1)
    {
      switch (GET_CODE (x))
	{
	case MEM:
	case CONST:
	  x = XEXP (x, 0);
	  break;
	case UNSPEC:
	  x = XVECEXP (x, 0, 0);
	  break;
	case PLUS:
	  if (GET_CODE (XEXP (x, 1)) != CONST_INT)
	    return 0;
	  x = XEXP (x, 0);
	  break;
	default:
	  goto done;
	}
    }
 done:
  if (GET_CODE (x) != SYMBOL_REF)
    return 0;
  name = XSTR (x, 0);
  if (name[0] == '@' && name[2] == '.')
    {
      if (name[1] == 'i' || name[1] == 'I')
	{
	  if (name[1] == 'I')
	    return 'f'; /* near */
	  return 'n'; /* far */
	}
      return name[1];
    }
  return 0;
}

int
mep_regno_reg_class (int regno)
{
  switch (regno)
    {
    case SP_REGNO:		return SP_REGS;
    case TP_REGNO:		return TP_REGS;
    case GP_REGNO:		return GP_REGS;
    case 0: 			return R0_REGS;
    case HI_REGNO:		return HI_REGS;
    case LO_REGNO:		return LO_REGS;
    case ARG_POINTER_REGNUM:	return GENERAL_REGS;
    }

  if (GR_REGNO_P (regno))
    return regno < FIRST_GR_REGNO + 8 ? TPREL_REGS : GENERAL_REGS;
  if (CONTROL_REGNO_P (regno))
    return CONTROL_REGS;

  if (CR_REGNO_P (regno))
    {
      int i, j;

      /* Search for the register amongst user-defined subclasses of
	 the coprocessor registers.  */
      for (i = USER0_REGS; i <= USER3_REGS; ++i)
	{
	  if (! TEST_HARD_REG_BIT (reg_class_contents[i], regno))
	    continue;
	  for (j = 0; j < N_REG_CLASSES; ++j)
	    {
	      enum reg_class sub = reg_class_subclasses[i][j];

	      if (sub == LIM_REG_CLASSES)
		return i;
	      if (TEST_HARD_REG_BIT (reg_class_contents[sub], regno))
		break;
	    }
	}

      return LOADABLE_CR_REGNO_P (regno) ? LOADABLE_CR_REGS : CR_REGS;
    }

  if (CCR_REGNO_P (regno))
    return CCR_REGS;

  gcc_assert (regno >= FIRST_SHADOW_REGISTER && regno <= LAST_SHADOW_REGISTER);
  return NO_REGS;
}

#if 0
int
mep_reg_class_from_constraint (int c, const char *str)
{
  switch (c)
    {
    case 'a':
      return SP_REGS;
    case 'b':
      return TP_REGS;
    case 'c':
      return CONTROL_REGS;
    case 'd':
      return HILO_REGS;
    case 'e':
      {
	switch (str[1])
	  {
	  case 'm':
	    return LOADABLE_CR_REGS;
	  case 'x':
	    return mep_have_copro_copro_moves_p ? CR_REGS : NO_REGS;
	  case 'r':
	    return mep_have_core_copro_moves_p ? CR_REGS : NO_REGS;
	  default:
	    return NO_REGS;
	  }
      }
    case 'h':
      return HI_REGS;
    case 'j':
      return RPC_REGS;
    case 'l':
      return LO_REGS;
    case 't':
      return TPREL_REGS;
    case 'v':
      return GP_REGS;
    case 'x':
      return CR_REGS;
    case 'y':
      return CCR_REGS;
    case 'z':
      return R0_REGS;

    case 'A':
    case 'B':
    case 'C':
    case 'D':
      {
	enum reg_class which = c - 'A' + USER0_REGS;
	return (reg_class_size[which] > 0 ? which : NO_REGS);
      }

    default:
      return NO_REGS;
    }
}

bool
mep_const_ok_for_letter_p (HOST_WIDE_INT value, int c)
{
  switch (c)
    {
      case 'I': return value >= -32768 && value <      32768;
      case 'J': return value >=      0 && value <      65536;
      case 'K': return value >=      0 && value < 0x01000000;
      case 'L': return value >=    -32 && value <         32;
      case 'M': return value >=      0 && value <         32;
      case 'N': return value >=      0 && value <         16;
      case 'O':
	if (value & 0xffff)
	  return false;
	return value >= -2147483647-1 && value <= 2147483647;
    default:
      gcc_unreachable ();
    }
}

bool
mep_extra_constraint (rtx value, int c)
{
  encode_pattern (value);

  switch (c)
    {
    case 'R':
      /* For near symbols, like what call uses.  */
      if (GET_CODE (value) == REG)
	return 0;
      return mep_call_address_operand (value, GET_MODE (value));

    case 'S':
      /* For signed 8-bit immediates.  */
      return (GET_CODE (value) == CONST_INT
	      && INTVAL (value) >= -128
	      && INTVAL (value) <= 127);

    case 'T':
      /* For tp/gp relative symbol values.  */
      return (RTX_IS ("u3s") || RTX_IS ("u2s")
              || RTX_IS ("+u3si") || RTX_IS ("+u2si"));

    case 'U':
      /* Non-absolute memories.  */
      return GET_CODE (value) == MEM && ! CONSTANT_P (XEXP (value, 0));

    case 'W':
      /* %hi(sym) */
      return RTX_IS ("Hs");

    case 'Y':
      /* Register indirect.  */
      return RTX_IS ("mr");

    case 'Z':
      return mep_section_tag (value) == 'c' && RTX_IS ("ms");
    }

  return false;
}
#endif

#undef PASS
#undef FAIL

static bool
const_in_range (rtx x, int minv, int maxv)
{
  return (GET_CODE (x) == CONST_INT
	  && INTVAL (x) >= minv
	  && INTVAL (x) <= maxv);
}

/* Given three integer registers DEST, SRC1 and SRC2, return an rtx X
   such that "mulr DEST,X" will calculate DEST = SRC1 * SRC2.  If a move
   is needed, emit it before INSN if INSN is nonnull, otherwise emit it
   at the end of the insn stream.  */

rtx
mep_mulr_source (rtx insn, rtx dest, rtx src1, rtx src2)
{
  if (rtx_equal_p (dest, src1))
    return src2;
  else if (rtx_equal_p (dest, src2))
    return src1;
  else
    {
      if (insn == 0)
	emit_insn (gen_movsi (copy_rtx (dest), src1));
      else
	emit_insn_before (gen_movsi (copy_rtx (dest), src1), insn);
      return src2;
    }
}

/* Replace INSN's pattern with PATTERN, a multiplication PARALLEL.
   Change the last element of PATTERN from (clobber (scratch:SI))
   to (clobber (reg:SI HI_REGNO)).  */

static void
mep_rewrite_mult (rtx insn, rtx pattern)
{
  rtx hi_clobber;

  hi_clobber = XVECEXP (pattern, 0, XVECLEN (pattern, 0) - 1);
  XEXP (hi_clobber, 0) = gen_rtx_REG (SImode, HI_REGNO);
  PATTERN (insn) = pattern;
  INSN_CODE (insn) = -1;
}

/* Subroutine of mep_reuse_lo_p.  Rewrite instruction INSN so that it
   calculates SRC1 * SRC2 and stores the result in $lo.  Also make it
   store the result in DEST if nonnull.  */

static void
mep_rewrite_mulsi3 (rtx insn, rtx dest, rtx src1, rtx src2)
{
  rtx lo, pattern;

  lo = gen_rtx_REG (SImode, LO_REGNO);
  if (dest)
    pattern = gen_mulsi3r (lo, dest, copy_rtx (dest),
			   mep_mulr_source (insn, dest, src1, src2));
  else
    pattern = gen_mulsi3_lo (lo, src1, src2);
  mep_rewrite_mult (insn, pattern);
}

/* Like mep_rewrite_mulsi3, but calculate SRC1 * SRC2 + SRC3.  First copy
   SRC3 into $lo, then use either madd or maddr.  The move into $lo will
   be deleted by a peephole2 if SRC3 is already in $lo.  */

static void
mep_rewrite_maddsi3 (rtx insn, rtx dest, rtx src1, rtx src2, rtx src3)
{
  rtx lo, pattern;

  lo = gen_rtx_REG (SImode, LO_REGNO);
  emit_insn_before (gen_movsi (copy_rtx (lo), src3), insn);
  if (dest)
    pattern = gen_maddsi3r (lo, dest, copy_rtx (dest),
			    mep_mulr_source (insn, dest, src1, src2),
			    copy_rtx (lo));
  else
    pattern = gen_maddsi3_lo (lo, src1, src2, copy_rtx (lo));
  mep_rewrite_mult (insn, pattern);
}

/* Return true if $lo has the same value as integer register GPR when
   instruction INSN is reached.  If necessary, rewrite the instruction
   that sets $lo so that it uses a proper SET, not a CLOBBER.  LO is an
   rtx for (reg:SI LO_REGNO).

   This function is intended to be used by the peephole2 pass.  Since
   that pass goes from the end of a basic block to the beginning, and
   propagates liveness information on the way, there is no need to
   update register notes here.

   If GPR_DEAD_P is true on entry, and this function returns true,
   then the caller will replace _every_ use of GPR in and after INSN
   with LO.  This means that if the instruction that sets $lo is a
   mulr- or maddr-type instruction, we can rewrite it to use mul or
   madd instead.  In combination with the copy progagation pass,
   this allows us to replace sequences like:

	mov GPR,R1
	mulr GPR,R2

   with:

	mul R1,R2

   if GPR is no longer used.  */

static bool
mep_reuse_lo_p_1 (rtx lo, rtx gpr, rtx insn, bool gpr_dead_p)
{
  do
    {
      insn = PREV_INSN (insn);
      if (INSN_P (insn))
	switch (recog_memoized (insn))
	  {
	  case CODE_FOR_mulsi3_1:
	    extract_insn (insn);
	    if (rtx_equal_p (recog_data.operand[0], gpr))
	      {
		mep_rewrite_mulsi3 (insn,
				    gpr_dead_p ? NULL : recog_data.operand[0],
				    recog_data.operand[1],
				    recog_data.operand[2]);
		return true;
	      }
	    return false;

	  case CODE_FOR_maddsi3:
	    extract_insn (insn);
	    if (rtx_equal_p (recog_data.operand[0], gpr))
	      {
		mep_rewrite_maddsi3 (insn,
				     gpr_dead_p ? NULL : recog_data.operand[0],
				     recog_data.operand[1],
				     recog_data.operand[2],
				     recog_data.operand[3]);
		return true;
	      }
	    return false;

	  case CODE_FOR_mulsi3r:
	  case CODE_FOR_maddsi3r:
	    extract_insn (insn);
	    return rtx_equal_p (recog_data.operand[1], gpr);

	  default:
	    if (reg_set_p (lo, insn)
		|| reg_set_p (gpr, insn)
		|| volatile_insn_p (PATTERN (insn)))
	      return false;

	    if (gpr_dead_p && reg_referenced_p (gpr, PATTERN (insn)))
	      gpr_dead_p = false;
	    break;
	  }
    }
  while (!NOTE_INSN_BASIC_BLOCK_P (insn));
  return false;
}

/* A wrapper around mep_reuse_lo_p_1 that preserves recog_data.  */

bool
mep_reuse_lo_p (rtx lo, rtx gpr, rtx insn, bool gpr_dead_p)
{
  bool result = mep_reuse_lo_p_1 (lo, gpr, insn, gpr_dead_p);
  extract_insn (insn);
  return result;
}

/* Return true if SET can be turned into a post-modify load or store
   that adds OFFSET to GPR.  In other words, return true if SET can be
   changed into:

       (parallel [SET (set GPR (plus:SI GPR OFFSET))]).

   It's OK to change SET to an equivalent operation in order to
   make it match.  */

static bool
mep_use_post_modify_for_set_p (rtx set, rtx gpr, rtx offset)
{
  rtx *reg, *mem;
  unsigned int reg_bytes, mem_bytes;
  enum machine_mode reg_mode, mem_mode;

  /* Only simple SETs can be converted.  */
  if (GET_CODE (set) != SET)
    return false;

  /* Point REG to what we hope will be the register side of the set and
     MEM to what we hope will be the memory side.  */
  if (GET_CODE (SET_DEST (set)) == MEM)
    {
      mem = &SET_DEST (set);
      reg = &SET_SRC (set);
    }
  else
    {
      reg = &SET_DEST (set);
      mem = &SET_SRC (set);
      if (GET_CODE (*mem) == SIGN_EXTEND)
	mem = &XEXP (*mem, 0);
    }

  /* Check that *REG is a suitable coprocessor register.  */
  if (GET_CODE (*reg) != REG || !LOADABLE_CR_REGNO_P (REGNO (*reg)))
    return false;

  /* Check that *MEM is a suitable memory reference.  */
  if (GET_CODE (*mem) != MEM || !rtx_equal_p (XEXP (*mem, 0), gpr))
    return false;

  /* Get the number of bytes in each operand.  */
  mem_bytes = GET_MODE_SIZE (GET_MODE (*mem));
  reg_bytes = GET_MODE_SIZE (GET_MODE (*reg));

  /* Check that OFFSET is suitably aligned.  */
  if (INTVAL (offset) & (mem_bytes - 1))
    return false;

  /* Convert *MEM to a normal integer mode.  */
  mem_mode = mode_for_size (mem_bytes * BITS_PER_UNIT, MODE_INT, 0);
  *mem = change_address (*mem, mem_mode, NULL);

  /* Adjust *REG as well.  */
  *reg = shallow_copy_rtx (*reg);
  if (reg == &SET_DEST (set) && reg_bytes < UNITS_PER_WORD)
    {
      /* SET is a subword load.  Convert it to an explicit extension.  */
      PUT_MODE (*reg, SImode);
      *mem = gen_rtx_SIGN_EXTEND (SImode, *mem);
    }
  else
    {
      reg_mode = mode_for_size (reg_bytes * BITS_PER_UNIT, MODE_INT, 0);
      PUT_MODE (*reg, reg_mode);
    }
  return true;
}

/* Return the effect of frame-related instruction INSN.  */

static rtx
mep_frame_expr (rtx insn)
{
  rtx note, expr;

  note = find_reg_note (insn, REG_FRAME_RELATED_EXPR, 0);
  expr = (note != 0 ? XEXP (note, 0) : copy_rtx (PATTERN (insn)));
  RTX_FRAME_RELATED_P (expr) = 1;
  return expr;
}

/* Merge instructions INSN1 and INSN2 using a PARALLEL.  Store the
   new pattern in INSN1; INSN2 will be deleted by the caller.  */

static void
mep_make_parallel (rtx insn1, rtx insn2)
{
  rtx expr;

  if (RTX_FRAME_RELATED_P (insn2))
    {
      expr = mep_frame_expr (insn2);
      if (RTX_FRAME_RELATED_P (insn1))
	expr = gen_rtx_SEQUENCE (VOIDmode,
				 gen_rtvec (2, mep_frame_expr (insn1), expr));
      set_unique_reg_note (insn1, REG_FRAME_RELATED_EXPR, expr);
      RTX_FRAME_RELATED_P (insn1) = 1;
    }

  PATTERN (insn1) = gen_rtx_PARALLEL (VOIDmode,
				      gen_rtvec (2, PATTERN (insn1),
						 PATTERN (insn2)));
  INSN_CODE (insn1) = -1;
}

/* SET_INSN is an instruction that adds OFFSET to REG.  Go back through
   the basic block to see if any previous load or store instruction can
   be persuaded to do SET_INSN as a side-effect.  Return true if so.  */

static bool
mep_use_post_modify_p_1 (rtx set_insn, rtx reg, rtx offset)
{
  rtx insn;

  insn = set_insn;
  do
    {
      insn = PREV_INSN (insn);
      if (INSN_P (insn))
	{
	  if (mep_use_post_modify_for_set_p (PATTERN (insn), reg, offset))
	    {
	      mep_make_parallel (insn, set_insn);
	      return true;
	    }

	  if (reg_set_p (reg, insn)
	      || reg_referenced_p (reg, PATTERN (insn))
	      || volatile_insn_p (PATTERN (insn)))
	    return false;
	}
    }
  while (!NOTE_INSN_BASIC_BLOCK_P (insn));
  return false;
}

/* A wrapper around mep_use_post_modify_p_1 that preserves recog_data.  */

bool
mep_use_post_modify_p (rtx insn, rtx reg, rtx offset)
{
  bool result = mep_use_post_modify_p_1 (insn, reg, offset);
  extract_insn (insn);
  return result;
}

bool
mep_allow_clip (rtx ux, rtx lx, int s)
{
  HOST_WIDE_INT u = INTVAL (ux);
  HOST_WIDE_INT l = INTVAL (lx);
  int i;

  if (!TARGET_OPT_CLIP)
    return false;

  if (s)
    {
      for (i = 0; i < 30; i ++)
	if ((u == ((HOST_WIDE_INT) 1 << i) - 1)
	    && (l == - ((HOST_WIDE_INT) 1 << i)))
	  return true;
    }
  else
    {
      if (l != 0)
	return false;

      for (i = 0; i < 30; i ++)
	if ((u == ((HOST_WIDE_INT) 1 << i) - 1))
	  return true;
    }
  return false;
}

bool
mep_bit_position_p (rtx x, bool looking_for)
{
  if (GET_CODE (x) != CONST_INT)
    return false;
  switch ((int) INTVAL(x) & 0xff)
    {
    case 0x01: case 0x02: case 0x04: case 0x08:
    case 0x10: case 0x20: case 0x40: case 0x80:
      return looking_for;
    case 0xfe: case 0xfd: case 0xfb: case 0xf7:
    case 0xef: case 0xdf: case 0xbf: case 0x7f:
      return !looking_for;
    }
  return false;
}

static bool
move_needs_splitting (rtx dest, rtx src,
		      enum machine_mode mode ATTRIBUTE_UNUSED)
{
  int s = mep_section_tag (src);

  while (1)
    {
      if (GET_CODE (src) == CONST
	  || GET_CODE (src) == MEM)
	src = XEXP (src, 0);
      else if (GET_CODE (src) == SYMBOL_REF
	       || GET_CODE (src) == LABEL_REF
	       || GET_CODE (src) == PLUS)
	break;
      else
	return false;
    }
  if (s == 'f'
      || (GET_CODE (src) == PLUS
	  && GET_CODE (XEXP (src, 1)) == CONST_INT
	  && (INTVAL (XEXP (src, 1)) < -65536
	      || INTVAL (XEXP (src, 1)) > 0xffffff))
      || (GET_CODE (dest) == REG
	  && REGNO (dest) > 7 && REGNO (dest) < FIRST_PSEUDO_REGISTER))
    return true;
  return false;
}

bool
mep_split_mov (rtx *operands, int symbolic)
{
  if (symbolic)
    {
      if (move_needs_splitting (operands[0], operands[1], SImode))
	return true;
      return false;
    }

  if (GET_CODE (operands[1]) != CONST_INT)
    return false;

  if (constraint_satisfied_p (operands[1], CONSTRAINT_I)
      || constraint_satisfied_p (operands[1], CONSTRAINT_J)
      || constraint_satisfied_p (operands[1], CONSTRAINT_O))
    return false;

  if (((!reload_completed && !reload_in_progress)
       || (REG_P (operands[0]) && REGNO (operands[0]) < 8))
      && constraint_satisfied_p (operands[1], CONSTRAINT_K))
    return false;

  return true;
}

/* Irritatingly, the "jsrv" insn *toggles* PSW.OM rather than set
   it to one specific value.  So the insn chosen depends on whether
   the source and destination modes match.  */

bool
mep_vliw_mode_match (rtx tgt)
{
  bool src_vliw = mep_vliw_function_p (cfun->decl);
  bool tgt_vliw = INTVAL (tgt);

  return src_vliw == tgt_vliw;
}

/* Like the above, but also test for near/far mismatches.  */

bool
mep_vliw_jmp_match (rtx tgt)
{
  bool src_vliw = mep_vliw_function_p (cfun->decl);
  bool tgt_vliw = INTVAL (tgt);

  if (mep_section_tag (DECL_RTL (cfun->decl)) == 'f')
    return false;

  return src_vliw == tgt_vliw;
}

bool
mep_multi_slot (rtx x)
{
  return get_attr_slot (x) == SLOT_MULTI;
}


bool
mep_legitimate_constant_p (rtx x)
{
  /* We can't convert symbol values to gp- or tp-rel values after
     reload, as reload might have used $gp or $tp for other
     purposes.  */
  if (GET_CODE (x) == SYMBOL_REF && (reload_in_progress || reload_completed))
    {
      char e = mep_section_tag (x);
      return (e != 't' && e != 'b');
    }
  return 1;
}

/* Be careful not to use macros that need to be compiled one way for
   strict, and another way for not-strict, like REG_OK_FOR_BASE_P.  */

bool
mep_legitimate_address (enum machine_mode mode, rtx x, int strict)
{
  int the_tag;

#define DEBUG_LEGIT 0
#if DEBUG_LEGIT
  fprintf (stderr, "legit: mode %s strict %d ", mode_name[mode], strict);
  debug_rtx (x);
#endif

  if (GET_CODE (x) == LO_SUM
      && GET_CODE (XEXP (x, 0)) == REG
      && GEN_REG (REGNO (XEXP (x, 0)), strict)
      && CONSTANT_P (XEXP (x, 1)))
    {
      if (GET_MODE_SIZE (mode) > 4)
	{
	  /* We will end up splitting this, and lo_sums are not
	     offsettable for us.  */
#if DEBUG_LEGIT
	  fprintf(stderr, " - nope, %%lo(sym)[reg] not splittable\n");
#endif
	  return false;
	}
#if DEBUG_LEGIT
      fprintf (stderr, " - yup, %%lo(sym)[reg]\n");
#endif
      return true;
    }

  if (GET_CODE (x) == REG
      && GEN_REG (REGNO (x), strict))
    {
#if DEBUG_LEGIT
      fprintf (stderr, " - yup, [reg]\n");
#endif
      return true;
    }

  if (GET_CODE (x) == PLUS
      && GET_CODE (XEXP (x, 0)) == REG
      && GEN_REG (REGNO (XEXP (x, 0)), strict)
      && const_in_range (XEXP (x, 1), -32768, 32767))
    {
#if DEBUG_LEGIT
      fprintf (stderr, " - yup, [reg+const]\n");
#endif
      return true;
    }

  if (GET_CODE (x) == PLUS
      && GET_CODE (XEXP (x, 0)) == REG
      && GEN_REG (REGNO (XEXP (x, 0)), strict)
      && GET_CODE (XEXP (x, 1)) == CONST
      && (GET_CODE (XEXP (XEXP (x, 1), 0)) == UNSPEC
	  || (GET_CODE (XEXP (XEXP (x, 1), 0)) == PLUS
	      && GET_CODE (XEXP (XEXP (XEXP (x, 1), 0), 0)) == UNSPEC
	      && GET_CODE (XEXP (XEXP (XEXP (x, 1), 0), 1)) == CONST_INT)))
    {
#if DEBUG_LEGIT
      fprintf (stderr, " - yup, [reg+unspec]\n");
#endif
      return true;
    }

  the_tag = mep_section_tag (x);

  if (the_tag == 'f')
    {
#if DEBUG_LEGIT
      fprintf (stderr, " - nope, [far]\n");
#endif
      return false;
    }

  if (mode == VOIDmode
      && GET_CODE (x) == SYMBOL_REF)
    {
#if DEBUG_LEGIT
      fprintf (stderr, " - yup, call [symbol]\n");
#endif
      return true;
    }

  if ((mode == SImode || mode == SFmode)
      && CONSTANT_P (x)
      && LEGITIMATE_CONSTANT_P (x)
      && the_tag != 't' && the_tag != 'b')
    {
      if (GET_CODE (x) != CONST_INT
	  || (INTVAL (x) <= 0xfffff
	      && INTVAL (x) >= 0
	      && (INTVAL (x) % 4) == 0))
	{
#if DEBUG_LEGIT
	  fprintf (stderr, " - yup, [const]\n");
#endif
	  return true;
	}
    }

#if DEBUG_LEGIT
  fprintf (stderr, " - nope.\n");
#endif
  return false;
}

int
mep_legitimize_reload_address (rtx *x, enum machine_mode mode, int opnum,
			       int type_i,
			       int ind_levels ATTRIBUTE_UNUSED)
{
  enum reload_type type = (enum reload_type) type_i;

  if (GET_CODE (*x) == PLUS
      && GET_CODE (XEXP (*x, 0)) == MEM
      && GET_CODE (XEXP (*x, 1)) == REG)
    {
      /* GCC will by default copy the MEM into a REG, which results in
	 an invalid address.  For us, the best thing to do is move the
	 whole expression to a REG.  */
      push_reload (*x, NULL_RTX, x, NULL,
		   GENERAL_REGS, mode, VOIDmode,
		   0, 0, opnum, type);
      return 1;
    }

  if (GET_CODE (*x) == PLUS
      && GET_CODE (XEXP (*x, 0)) == SYMBOL_REF
      && GET_CODE (XEXP (*x, 1)) == CONST_INT)
    {
      char e = mep_section_tag (XEXP (*x, 0));

      if (e != 't' && e != 'b')
	{
	  /* GCC thinks that (sym+const) is a valid address.  Well,
	     sometimes it is, this time it isn't.  The best thing to
	     do is reload the symbol to a register, since reg+int
	     tends to work, and we can't just add the symbol and
	     constant anyway.  */
	  push_reload (XEXP (*x, 0), NULL_RTX, &(XEXP(*x, 0)), NULL,
		       GENERAL_REGS, mode, VOIDmode,
		       0, 0, opnum, type);
	  return 1;
	}
    }
  return 0;
}

int
mep_core_address_length (rtx insn, int opn)
{
  rtx set = single_set (insn);
  rtx mem = XEXP (set, opn);
  rtx other = XEXP (set, 1-opn);
  rtx addr = XEXP (mem, 0);

  if (register_operand (addr, Pmode))
    return 2;
  if (GET_CODE (addr) == PLUS)
    {
      rtx addend = XEXP (addr, 1);

      gcc_assert (REG_P (XEXP (addr, 0)));

      switch (REGNO (XEXP (addr, 0)))
	{
	case STACK_POINTER_REGNUM:
	  if (GET_MODE_SIZE (GET_MODE (mem)) == 4
	      && mep_imm7a4_operand (addend, VOIDmode))
	    return 2;
	  break;

	case 13: /* TP */
	  gcc_assert (REG_P (other));

	  if (REGNO (other) >= 8)
	    break;

	  if (GET_CODE (addend) == CONST
	      && GET_CODE (XEXP (addend, 0)) == UNSPEC
	      && XINT (XEXP (addend, 0), 1) == UNS_TPREL)
	    return 2;

	  if (GET_CODE (addend) == CONST_INT
	      && INTVAL (addend) >= 0
	      && INTVAL (addend) <= 127
	      && INTVAL (addend) % GET_MODE_SIZE (GET_MODE (mem)) == 0)
	    return 2;
	  break;
	}
    }

  return 4;
}

int
mep_cop_address_length (rtx insn, int opn)
{
  rtx set = single_set (insn);
  rtx mem = XEXP (set, opn);
  rtx addr = XEXP (mem, 0);

  if (GET_CODE (mem) != MEM)
    return 2;
  if (register_operand (addr, Pmode))
    return 2;
  if (GET_CODE (addr) == POST_INC)
    return 2;

  return 4;
}

#define DEBUG_EXPAND_MOV 0
bool
mep_expand_mov (rtx *operands, enum machine_mode mode)
{
  int i, t;
  int tag[2];
  rtx tpsym, tpoffs;
  int post_reload = 0;

  tag[0] = mep_section_tag (operands[0]);
  tag[1] = mep_section_tag (operands[1]);

  if (!reload_in_progress
      && !reload_completed
      && GET_CODE (operands[0]) != REG
      && GET_CODE (operands[0]) != SUBREG
      && GET_CODE (operands[1]) != REG
      && GET_CODE (operands[1]) != SUBREG)
    operands[1] = copy_to_mode_reg (mode, operands[1]);
  
#if DEBUG_EXPAND_MOV
  fprintf(stderr, "expand move %s %d\n", mode_name[mode],
	  reload_in_progress || reload_completed);
  debug_rtx (operands[0]);
  debug_rtx (operands[1]);
#endif

  if (mode == DImode || mode == DFmode)
    return false;

  if (reload_in_progress || reload_completed)
    {
      rtx r;

      if (GET_CODE (operands[0]) == REG && REGNO (operands[0]) == TP_REGNO)
	cfun->machine->reload_changes_tp = true;

      if (tag[0] == 't' || tag[1] == 't')
	{
	  r = has_hard_reg_initial_val (Pmode, GP_REGNO);
	  if (!r || GET_CODE (r) != REG || REGNO (r) != GP_REGNO)
	    post_reload = 1;
	}
      if (tag[0] == 'b' || tag[1] == 'b')
	{
	  r = has_hard_reg_initial_val (Pmode, TP_REGNO);
	  if (!r || GET_CODE (r) != REG || REGNO (r) != TP_REGNO)
	    post_reload = 1;
	}
      if (cfun->machine->reload_changes_tp == true)
	post_reload = 1;
    }

  if (!post_reload)
    {
      rtx n;
      if (symbol_p (operands[1]))
	{
	  t = mep_section_tag (operands[1]);
	  if (t == 'b' || t == 't')
	    {

	      if (GET_CODE (operands[1]) == SYMBOL_REF)
		{
		  tpsym = operands[1];
		  n = gen_rtx_UNSPEC (mode,
				      gen_rtvec (1, operands[1]),
				      t == 'b' ? UNS_TPREL : UNS_GPREL);
		  n = gen_rtx_CONST (mode, n);
		}
	      else if (GET_CODE (operands[1]) == CONST
		       && GET_CODE (XEXP (operands[1], 0)) == PLUS
		       && GET_CODE (XEXP (XEXP (operands[1], 0), 0)) == SYMBOL_REF
		       && GET_CODE (XEXP (XEXP (operands[1], 0), 1)) == CONST_INT)
		{
		  tpsym = XEXP (XEXP (operands[1], 0), 0);
		  tpoffs = XEXP (XEXP (operands[1], 0), 1);
		  n = gen_rtx_UNSPEC (mode,
				      gen_rtvec (1, tpsym),
				      t == 'b' ? UNS_TPREL : UNS_GPREL);
		  n = gen_rtx_PLUS (mode, n, tpoffs);
		  n = gen_rtx_CONST (mode, n);
		}
	      else if (GET_CODE (operands[1]) == CONST
		       && GET_CODE (XEXP (operands[1], 0)) == UNSPEC)
		return false;
	      else
		{
		  error ("unusual TP-relative address");
		  return false;
		}

	      n = gen_rtx_PLUS (mode, (t == 'b' ? mep_tp_rtx ()
				       : mep_gp_rtx ()), n);
	      n = emit_insn (gen_rtx_SET (mode, operands[0], n));
#if DEBUG_EXPAND_MOV
	      fprintf(stderr, "mep_expand_mov emitting ");
	      debug_rtx(n);
#endif
	      return true;
	    }
	}

      for (i=0; i < 2; i++)
	{
	  t = mep_section_tag (operands[i]);
	  if (GET_CODE (operands[i]) == MEM && (t == 'b' || t == 't'))
	    {
	      rtx sym, n, r;
	      int u;

	      sym = XEXP (operands[i], 0);
	      if (GET_CODE (sym) == CONST
		  && GET_CODE (XEXP (sym, 0)) == UNSPEC)
		sym = XVECEXP (XEXP (sym, 0), 0, 0);

	      if (t == 'b')
		{
		  r = mep_tp_rtx ();
		  u = UNS_TPREL;
		}
	      else
		{
		  r = mep_gp_rtx ();
		  u = UNS_GPREL;
		}

	      n = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, sym), u);
	      n = gen_rtx_CONST (Pmode, n);
	      n = gen_rtx_PLUS (Pmode, r, n);
	      operands[i] = replace_equiv_address (operands[i], n);
	    }
	}
    }

  if ((GET_CODE (operands[1]) != REG
       && MEP_CONTROL_REG (operands[0]))
      || (GET_CODE (operands[0]) != REG
	  && MEP_CONTROL_REG (operands[1])))
    {
      rtx temp;
#if DEBUG_EXPAND_MOV
      fprintf (stderr, "cr-mem, forcing op1 to reg\n");
#endif
      temp = gen_reg_rtx (mode);
      emit_move_insn (temp, operands[1]);
      operands[1] = temp;
    }

  if (symbolref_p (operands[0])
      && (mep_section_tag (XEXP (operands[0], 0)) == 'f'
	  || (GET_MODE_SIZE (mode) != 4)))
    {
      rtx temp;

      gcc_assert (!reload_in_progress && !reload_completed);

      temp = force_reg (Pmode, XEXP (operands[0], 0));
      operands[0] = replace_equiv_address (operands[0], temp);
      emit_move_insn (operands[0], operands[1]);
      return true;
    }

  if (!post_reload && (tag[1] == 't' || tag[1] == 'b'))
    tag[1] = 0;

  if (symbol_p (operands[1])
      && (tag[1] == 'f' || tag[1] == 't' || tag[1] == 'b'))
    {
      emit_insn (gen_movsi_topsym_s (operands[0], operands[1]));
      emit_insn (gen_movsi_botsym_s (operands[0], operands[0], operands[1]));
      return true;
    }

  if (symbolref_p (operands[1])
      && (tag[1] == 'f' || tag[1] == 't' || tag[1] == 'b'))
    {
      rtx temp;

      if (reload_in_progress || reload_completed)
	temp = operands[0];
      else
	temp = gen_reg_rtx (Pmode);

      emit_insn (gen_movsi_topsym_s (temp, operands[1]));
      emit_insn (gen_movsi_botsym_s (temp, temp, operands[1]));
      emit_move_insn (operands[0], replace_equiv_address (operands[1], temp));
      return true;
    }

  return false;
}

/* Cases where the pattern can't be made to use at all.  */

bool
mep_mov_ok (rtx *operands, enum machine_mode mode ATTRIBUTE_UNUSED)
{
  int i;

#define DEBUG_MOV_OK 0
#if DEBUG_MOV_OK
  fprintf (stderr, "mep_mov_ok %s %c=%c\n", mode_name[mode], mep_section_tag (operands[0]),
	   mep_section_tag (operands[1]));
  debug_rtx (operands[0]);
  debug_rtx (operands[1]);
#endif

  /* We want the movh patterns to get these.  */
  if (GET_CODE (operands[1]) == HIGH)
    return false;

  /* We can't store a register to a far variable without using a
     scratch register to hold the address.  Using far variables should
     be split by mep_emit_mov anyway.  */
  if (mep_section_tag (operands[0]) == 'f'
      || mep_section_tag (operands[1]) == 'f')
    {
#if DEBUG_MOV_OK
      fprintf (stderr, " - no, f\n");
#endif
      return false;
    }
  i = mep_section_tag (operands[1]);
  if ((i == 'b' || i == 't') && !reload_completed && !reload_in_progress)
    /* These are supposed to be generated with adds of the appropriate
       register.  During and after reload, however, we allow them to
       be accessed as normal symbols because adding a dependency on
       the base register now might cause problems.  */
    {
#if DEBUG_MOV_OK
      fprintf (stderr, " - no, bt\n");
#endif
      return false;
    }

  /* The only moves we can allow involve at least one general
     register, so require it.  */
  for (i = 0; i < 2; i ++)
    {
      /* Allow subregs too, before reload.  */
      rtx x = operands[i];

      if (GET_CODE (x) == SUBREG)
	x = XEXP (x, 0);
      if (GET_CODE (x) == REG
	  && ! MEP_CONTROL_REG (x))
	{
#if DEBUG_MOV_OK
	  fprintf (stderr, " - ok\n");
#endif
	  return true;
	}
    }
#if DEBUG_MOV_OK
  fprintf (stderr, " - no, no gen reg\n");
#endif
  return false;
}

#define DEBUG_SPLIT_WIDE_MOVE 0
void
mep_split_wide_move (rtx *operands, enum machine_mode mode)
{
  int i;

#if DEBUG_SPLIT_WIDE_MOVE
  fprintf (stderr, "\n\033[34mmep_split_wide_move\033[0m mode %s\n", mode_name[mode]);
  debug_rtx (operands[0]);
  debug_rtx (operands[1]);
#endif

  for (i = 0; i <= 1; i++)
    {
      rtx op = operands[i], hi, lo;

      switch (GET_CODE (op))
	{
	case REG:
	  {
	    unsigned int regno = REGNO (op);

	    if (TARGET_64BIT_CR_REGS && CR_REGNO_P (regno))
	      {
		rtx i32;

		lo = gen_rtx_REG (SImode, regno);
		i32 = GEN_INT (32);
		hi = gen_rtx_ZERO_EXTRACT (SImode,
					   gen_rtx_REG (DImode, regno),
					   i32, i32);
	      }
	    else
	      {
		hi = gen_rtx_REG (SImode, regno + TARGET_LITTLE_ENDIAN);
		lo = gen_rtx_REG (SImode, regno + TARGET_BIG_ENDIAN);
	      }
	  }
	  break;

	case CONST_INT:
	case CONST_DOUBLE:
	case MEM:
	  hi = operand_subword (op, TARGET_LITTLE_ENDIAN, 0, mode);
	  lo = operand_subword (op, TARGET_BIG_ENDIAN, 0, mode);
	  break;

	default:
	  gcc_unreachable ();
	}

      /* The high part of CR <- GPR moves must be done after the low part.  */
      operands [i + 4] = lo;
      operands [i + 2] = hi;
    }

  if (reg_mentioned_p (operands[2], operands[5])
      || GET_CODE (operands[2]) == ZERO_EXTRACT
      || GET_CODE (operands[4]) == ZERO_EXTRACT)
    {
      rtx tmp;

      /* Overlapping register pairs -- make sure we don't
	 early-clobber ourselves.  */
      tmp = operands[2];
      operands[2] = operands[4];
      operands[4] = tmp;
      tmp = operands[3];
      operands[3] = operands[5];
      operands[5] = tmp;
    }

#if DEBUG_SPLIT_WIDE_MOVE
  fprintf(stderr, "\033[34m");
  debug_rtx (operands[2]);
  debug_rtx (operands[3]);
  debug_rtx (operands[4]);
  debug_rtx (operands[5]);
  fprintf(stderr, "\033[0m");
#endif
}

/* Emit a setcc instruction in its entirity.  */

static bool
mep_expand_setcc_1 (enum rtx_code code, rtx dest, rtx op1, rtx op2)
{
  rtx tmp;

  switch (code)
    {
    case GT:
    case GTU:
      tmp = op1, op1 = op2, op2 = tmp;
      code = swap_condition (code);
      /* FALLTHRU */

    case LT:
    case LTU:
      op1 = force_reg (SImode, op1);
      emit_insn (gen_rtx_SET (VOIDmode, dest,
			      gen_rtx_fmt_ee (code, SImode, op1, op2)));
      return true;

    case EQ:
      if (op2 != const0_rtx)
	op1 = expand_binop (SImode, sub_optab, op1, op2, NULL, 1, OPTAB_WIDEN);
      mep_expand_setcc_1 (LTU, dest, op1, const1_rtx);
      return true;

    case NE:
      /* Branchful sequence:
		mov dest, 0		16-bit
		beq op1, op2, Lover	16-bit (op2 < 16), 32-bit otherwise
		mov dest, 1		16-bit

	 Branchless sequence:
		add3 tmp, op1, -op2	32-bit (or mov + sub)
		sltu3 tmp, tmp, 1	16-bit
		xor3 dest, tmp, 1	32-bit
	*/
      if (optimize_size && op2 != const0_rtx)
	return false;

      if (op2 != const0_rtx)
	op1 = expand_binop (SImode, sub_optab, op1, op2, NULL, 1, OPTAB_WIDEN);

      op2 = gen_reg_rtx (SImode);
      mep_expand_setcc_1 (LTU, op2, op1, const1_rtx);

      emit_insn (gen_rtx_SET (VOIDmode, dest,
			      gen_rtx_XOR (SImode, op2, const1_rtx)));
      return true;

    case LE:
      if (GET_CODE (op2) != CONST_INT
	  || INTVAL (op2) == 0x7ffffff)
	return false;
      op2 = GEN_INT (INTVAL (op2) + 1);
      return mep_expand_setcc_1 (LT, dest, op1, op2);

    case LEU:
      if (GET_CODE (op2) != CONST_INT
	  || INTVAL (op2) == -1)
	return false;
      op2 = GEN_INT (trunc_int_for_mode (INTVAL (op2) + 1, SImode));
      return mep_expand_setcc_1 (LTU, dest, op1, op2);

    case GE:
      if (GET_CODE (op2) != CONST_INT
	  || INTVAL (op2) == trunc_int_for_mode (0x80000000, SImode))
	return false;
      op2 = GEN_INT (INTVAL (op2) - 1);
      return mep_expand_setcc_1 (GT, dest, op1, op2);

    case GEU:
      if (GET_CODE (op2) != CONST_INT
	  || op2 == const0_rtx)
	return false;
      op2 = GEN_INT (trunc_int_for_mode (INTVAL (op2) - 1, SImode));
      return mep_expand_setcc_1 (GTU, dest, op1, op2);

    default:
      gcc_unreachable ();
    }
}

bool
mep_expand_setcc (rtx *operands)
{
  rtx dest = operands[0];
  enum rtx_code code = GET_CODE (operands[1]);
  rtx op0 = operands[2];
  rtx op1 = operands[3];

  return mep_expand_setcc_1 (code, dest, op0, op1);
}

rtx
mep_expand_cbranch (rtx *operands)
{
  enum rtx_code code = GET_CODE (operands[0]);
  rtx op0 = operands[1];
  rtx op1 = operands[2];
  rtx tmp;

 restart:
  switch (code)
    {
    case LT:
      if (mep_imm4_operand (op1, SImode))
	break;

      tmp = gen_reg_rtx (SImode);
      gcc_assert (mep_expand_setcc_1 (LT, tmp, op0, op1));
      code = NE;
      op0 = tmp;
      op1 = const0_rtx;
      break;

    case GE:
      if (mep_imm4_operand (op1, SImode))
	break;

      tmp = gen_reg_rtx (SImode);
      gcc_assert (mep_expand_setcc_1 (LT, tmp, op0, op1));

      code = EQ;
      op0 = tmp;
      op1 = const0_rtx;
      break;

    case EQ:
    case NE:
      if (! mep_reg_or_imm4_operand (op1, SImode))
	op1 = force_reg (SImode, op1);
      break;

    case LE:
    case GT:
      if (GET_CODE (op1) == CONST_INT
	  && INTVAL (op1) != 0x7fffffff)
	{
	  op1 = GEN_INT (INTVAL (op1) + 1);
	  code = (code == LE ? LT : GE);
	  goto restart;
	}

      tmp = gen_reg_rtx (SImode);
      gcc_assert (mep_expand_setcc_1 (LT, tmp, op1, op0));

      code = (code == LE ? EQ : NE);
      op0 = tmp;
      op1 = const0_rtx;
      break;

    case LTU:
      if (op1 == const1_rtx)
	{
	  code = EQ;
	  op1 = const0_rtx;
	  break;
	}

      tmp = gen_reg_rtx (SImode);
      gcc_assert (mep_expand_setcc_1 (LTU, tmp, op0, op1));
      code = NE;
      op0 = tmp;
      op1 = const0_rtx;
      break;

    case LEU:
      tmp = gen_reg_rtx (SImode);
      if (mep_expand_setcc_1 (LEU, tmp, op0, op1))
	code = NE;
      else if (mep_expand_setcc_1 (LTU, tmp, op1, op0))
	code = EQ;
      else
	gcc_unreachable ();
      op0 = tmp;
      op1 = const0_rtx;
      break;

    case GTU:
      tmp = gen_reg_rtx (SImode);
      gcc_assert (mep_expand_setcc_1 (GTU, tmp, op0, op1)
		  || mep_expand_setcc_1 (LTU, tmp, op1, op0));
      code = NE;
      op0 = tmp;
      op1 = const0_rtx;
      break;

    case GEU:
      tmp = gen_reg_rtx (SImode);
      if (mep_expand_setcc_1 (GEU, tmp, op0, op1))
	code = NE;
      else if (mep_expand_setcc_1 (LTU, tmp, op0, op1))
	code = EQ;
      else
	gcc_unreachable ();
      op0 = tmp;
      op1 = const0_rtx;
      break;

    default:
      gcc_unreachable ();
    }

  return gen_rtx_fmt_ee (code, VOIDmode, op0, op1);
}

const char *
mep_emit_cbranch (rtx *operands, int ne)
{
  if (GET_CODE (operands[1]) == REG)
    return ne ? "bne\t%0, %1, %l2" : "beq\t%0, %1, %l2";
  else if (INTVAL (operands[1]) == 0 && !mep_vliw_function_p(cfun->decl))
    return ne ? "bnez\t%0, %l2" : "beqz\t%0, %l2";
  else
    return ne ? "bnei\t%0, %1, %l2" : "beqi\t%0, %1, %l2";
}

void
mep_expand_call (rtx *operands, int returns_value)
{
  rtx addr = operands[returns_value];
  rtx tp = mep_tp_rtx ();
  rtx gp = mep_gp_rtx ();

  gcc_assert (GET_CODE (addr) == MEM);

  addr = XEXP (addr, 0);

  if (! mep_call_address_operand (addr, VOIDmode))
    addr = force_reg (SImode, addr);

  if (! operands[returns_value+2])
    operands[returns_value+2] = const0_rtx;

  if (returns_value)
    emit_call_insn (gen_call_value_internal (operands[0], addr, operands[2],
					     operands[3], tp, gp));
  else
    emit_call_insn (gen_call_internal (addr, operands[1],
				       operands[2], tp, gp));
}

/* Aliasing Support.  */

/* If X is a machine specific address (i.e. a symbol or label being
   referenced as a displacement from the GOT implemented using an
   UNSPEC), then return the base term.  Otherwise return X.  */

rtx
mep_find_base_term (rtx x)
{
  rtx base, term;
  int unspec;

  if (GET_CODE (x) != PLUS)
    return x;
  base = XEXP (x, 0);
  term = XEXP (x, 1);

  if (has_hard_reg_initial_val(Pmode, TP_REGNO)
      && base == mep_tp_rtx ())
    unspec = UNS_TPREL;
  else if (has_hard_reg_initial_val(Pmode, GP_REGNO)
	   && base == mep_gp_rtx ())
    unspec = UNS_GPREL;
  else
    return x;

  if (GET_CODE (term) != CONST)
    return x;
  term = XEXP (term, 0);

  if (GET_CODE (term) != UNSPEC
      || XINT (term, 1) != unspec)
    return x;

  return XVECEXP (term, 0, 0);
}

/* Reload Support.  */

/* Return true if the registers in CLASS cannot represent the change from
   modes FROM to TO.  */

bool
mep_cannot_change_mode_class (enum machine_mode from, enum machine_mode to,
			       enum reg_class regclass)
{
  if (from == to)
    return false;

  /* 64-bit COP regs must remain 64-bit COP regs.  */
  if (TARGET_64BIT_CR_REGS
      && (regclass == CR_REGS
	  || regclass == LOADABLE_CR_REGS)
      && (GET_MODE_SIZE (to) < 8
	  || GET_MODE_SIZE (from) < 8))
    return true;

  return false;
}

#define MEP_NONGENERAL_CLASS(C) (!reg_class_subset_p (C, GENERAL_REGS))

static bool
mep_general_reg (rtx x)
{
  while (GET_CODE (x) == SUBREG)
    x = XEXP (x, 0);
  return GET_CODE (x) == REG && GR_REGNO_P (REGNO (x));
}

static bool
mep_nongeneral_reg (rtx x)
{
  while (GET_CODE (x) == SUBREG)
    x = XEXP (x, 0);
  return (GET_CODE (x) == REG
	  && !GR_REGNO_P (REGNO (x)) && REGNO (x) < FIRST_PSEUDO_REGISTER);
}

static bool
mep_general_copro_reg (rtx x)
{
  while (GET_CODE (x) == SUBREG)
    x = XEXP (x, 0);
  return (GET_CODE (x) == REG && CR_REGNO_P (REGNO (x)));
}

static bool
mep_nonregister (rtx x)
{
  while (GET_CODE (x) == SUBREG)
    x = XEXP (x, 0);
  return (GET_CODE (x) != REG || REGNO (x) >= FIRST_PSEUDO_REGISTER);
}

#define DEBUG_RELOAD 0

/* Return the secondary reload class needed for moving value X to or
   from a register in coprocessor register class CLASS.  */

static enum reg_class
mep_secondary_copro_reload_class (enum reg_class rclass, rtx x)
{
  if (mep_general_reg (x))
    /* We can do the move directly if mep_have_core_copro_moves_p,
       otherwise we need to go through memory.  Either way, no secondary
       register is needed.  */
    return NO_REGS;

  if (mep_general_copro_reg (x))
    {
      /* We can do the move directly if mep_have_copro_copro_moves_p.  */
      if (mep_have_copro_copro_moves_p)
	return NO_REGS;

      /* Otherwise we can use a temporary if mep_have_core_copro_moves_p.  */
      if (mep_have_core_copro_moves_p)
	return GENERAL_REGS;

      /* Otherwise we need to do it through memory.  No secondary
	 register is needed.  */
      return NO_REGS;
    }

  if (reg_class_subset_p (rclass, LOADABLE_CR_REGS)
      && constraint_satisfied_p (x, CONSTRAINT_U))
    /* X is a memory value that we can access directly.  */
    return NO_REGS;

  /* We have to move X into a GPR first and then copy it to
     the coprocessor register.  The move from the GPR to the
     coprocessor might be done directly or through memory,
     depending on mep_have_core_copro_moves_p. */
  return GENERAL_REGS;
}

/* Copying X to register in RCLASS.  */

enum reg_class
mep_secondary_input_reload_class (enum reg_class rclass,
				  enum machine_mode mode ATTRIBUTE_UNUSED,
				  rtx x)
{
  int rv = NO_REGS;

#if DEBUG_RELOAD
  fprintf (stderr, "secondary input reload copy to %s %s from ", reg_class_names[rclass], mode_name[mode]);
  debug_rtx (x);
#endif

  if (reg_class_subset_p (rclass, CR_REGS))
    rv = mep_secondary_copro_reload_class (rclass, x);
  else if (MEP_NONGENERAL_CLASS (rclass)
	   && (mep_nonregister (x) || mep_nongeneral_reg (x)))
    rv = GENERAL_REGS;

#if DEBUG_RELOAD
  fprintf (stderr, " - requires %s\n", reg_class_names[rv]);
#endif
  return (enum reg_class) rv;
}

/* Copying register in RCLASS to X.  */

enum reg_class
mep_secondary_output_reload_class (enum reg_class rclass,
				   enum machine_mode mode ATTRIBUTE_UNUSED,
				   rtx x)
{
  int rv = NO_REGS;

#if DEBUG_RELOAD
  fprintf (stderr, "secondary output reload copy from %s %s to ", reg_class_names[rclass], mode_name[mode]);
  debug_rtx (x);
#endif

  if (reg_class_subset_p (rclass, CR_REGS))
    rv = mep_secondary_copro_reload_class (rclass, x);
  else if (MEP_NONGENERAL_CLASS (rclass)
	   && (mep_nonregister (x) || mep_nongeneral_reg (x)))
    rv = GENERAL_REGS;

#if DEBUG_RELOAD
  fprintf (stderr, " - requires %s\n", reg_class_names[rv]);
#endif

  return (enum reg_class) rv;
}

/* Implement SECONDARY_MEMORY_NEEDED.  */

bool
mep_secondary_memory_needed (enum reg_class rclass1, enum reg_class rclass2,
			     enum machine_mode mode ATTRIBUTE_UNUSED)
{
  if (!mep_have_core_copro_moves_p)
    {
      if (reg_classes_intersect_p (rclass1, CR_REGS)
	  && reg_classes_intersect_p (rclass2, GENERAL_REGS))
	return true;
      if (reg_classes_intersect_p (rclass2, CR_REGS)
	  && reg_classes_intersect_p (rclass1, GENERAL_REGS))
	return true;
      if (!mep_have_copro_copro_moves_p
	  && reg_classes_intersect_p (rclass1, CR_REGS)
	  && reg_classes_intersect_p (rclass2, CR_REGS))
	return true;
    }
  return false;
}

void
mep_expand_reload (rtx *operands, enum machine_mode mode)
{
  /* There are three cases for each direction:
     register, farsym
     control, farsym
     control, nearsym */

  int s0 = mep_section_tag (operands[0]) == 'f';
  int s1 = mep_section_tag (operands[1]) == 'f';
  int c0 = mep_nongeneral_reg (operands[0]);
  int c1 = mep_nongeneral_reg (operands[1]);
  int which = (s0 ? 20:0) + (c0 ? 10:0) + (s1 ? 2:0) + (c1 ? 1:0);

#if DEBUG_RELOAD
  fprintf (stderr, "expand_reload %s\n", mode_name[mode]);
  debug_rtx (operands[0]);
  debug_rtx (operands[1]);
#endif

  switch (which)
    {
    case 00: /* Don't know why this gets here.  */
    case 02: /* general = far */
      emit_move_insn (operands[0], operands[1]);
      return;

    case 10: /* cr = mem */
    case 11: /* cr = cr */
    case 01: /* mem = cr */
    case 12: /* cr = far */
      emit_move_insn (operands[2], operands[1]);
      emit_move_insn (operands[0], operands[2]);
      return;

    case 20: /* far = general */
      emit_move_insn (operands[2], XEXP (operands[1], 0));
      emit_move_insn (operands[0], gen_rtx_MEM (mode, operands[2]));
      return;

    case 21: /* far = cr */
    case 22: /* far = far */
    default:
      fprintf (stderr, "unsupported expand reload case %02d for mode %s\n",
	       which, mode_name[mode]);
      debug_rtx (operands[0]);
      debug_rtx (operands[1]);
      gcc_unreachable ();
    }
}

/* Implement PREFERRED_RELOAD_CLASS.  See whether X is a constant that
   can be moved directly into registers 0 to 7, but not into the rest.
   If so, and if the required class includes registers 0 to 7, restrict
   it to those registers.  */

enum reg_class
mep_preferred_reload_class (rtx x, enum reg_class rclass)
{
  switch (GET_CODE (x))
    {
    case CONST_INT:
      if (INTVAL (x) >= 0x10000
	  && INTVAL (x) < 0x01000000
	  && (INTVAL (x) & 0xffff) != 0
	  && reg_class_subset_p (TPREL_REGS, rclass))
	rclass = TPREL_REGS;
      break;

    case CONST:
    case SYMBOL_REF:
    case LABEL_REF:
      if (mep_section_tag (x) != 'f'
	  && reg_class_subset_p (TPREL_REGS, rclass))
	rclass = TPREL_REGS;
      break;

    default:
      break;
    }
  return rclass;
}

/* Implement REGISTER_MOVE_COST.  Return 2 for direct single-register
   moves, 4 for direct double-register moves, and 1000 for anything
   that requires a temporary register or temporary stack slot.  */

int
mep_register_move_cost (enum machine_mode mode, enum reg_class from, enum reg_class to)
{
  if (mep_have_copro_copro_moves_p
      && reg_class_subset_p (from, CR_REGS)
      && reg_class_subset_p (to, CR_REGS))
    {
      if (TARGET_32BIT_CR_REGS && GET_MODE_SIZE (mode) > UNITS_PER_WORD)
	return 4;
      return 2;
    }
  if (reg_class_subset_p (from, CR_REGS)
      && reg_class_subset_p (to, CR_REGS))
    {
      if (TARGET_32BIT_CR_REGS && GET_MODE_SIZE (mode) > UNITS_PER_WORD)
	return 8;
      return 4;
    }
  if (reg_class_subset_p (from, CR_REGS)
      || reg_class_subset_p (to, CR_REGS))
    {
      if (GET_MODE_SIZE (mode) > UNITS_PER_WORD)
	return 4;
      return 2;
    }
  if (mep_secondary_memory_needed (from, to, mode))
    return 1000;
  if (MEP_NONGENERAL_CLASS (from) && MEP_NONGENERAL_CLASS (to))
    return 1000;

  if (GET_MODE_SIZE (mode) > 4)
    return 4;

  return 2;
}


/* Functions to save and restore machine-specific function data.  */

static struct machine_function *
mep_init_machine_status (void)
{
  return ggc_alloc_cleared_machine_function ();
}

static rtx
mep_allocate_initial_value (rtx reg)
{
  int rss;

  if (GET_CODE (reg) != REG)
    return NULL_RTX;

  if (REGNO (reg) >= FIRST_PSEUDO_REGISTER)
    return NULL_RTX;

  /* In interrupt functions, the "initial" values of $gp and $tp are
     provided by the prologue.  They are not necessarily the same as
     the values that the caller was using.  */
  if (REGNO (reg) == TP_REGNO || REGNO (reg) == GP_REGNO)
    if (mep_interrupt_p ())
      return NULL_RTX;

  if (! cfun->machine->reg_save_slot[REGNO(reg)])
    {
      cfun->machine->reg_save_size += 4;
      cfun->machine->reg_save_slot[REGNO(reg)] = cfun->machine->reg_save_size;
    }

  rss = cfun->machine->reg_save_slot[REGNO(reg)];
  return gen_rtx_MEM (SImode, plus_constant (arg_pointer_rtx, -rss));
}

rtx
mep_return_addr_rtx (int count)
{
  if (count != 0)
    return const0_rtx;

  return get_hard_reg_initial_val (Pmode, LP_REGNO);
}

static rtx
mep_tp_rtx (void)
{
  return get_hard_reg_initial_val (Pmode, TP_REGNO);
}

static rtx
mep_gp_rtx (void)
{
  return get_hard_reg_initial_val (Pmode, GP_REGNO);
}

static bool
mep_interrupt_p (void)
{
  if (cfun->machine->interrupt_handler == 0)
    {
      int interrupt_handler
	= (lookup_attribute ("interrupt",
			     DECL_ATTRIBUTES (current_function_decl))
	   != NULL_TREE);
      cfun->machine->interrupt_handler = interrupt_handler ? 2 : 1;
    }
  return cfun->machine->interrupt_handler == 2;
}

static bool
mep_disinterrupt_p (void)
{
  if (cfun->machine->disable_interrupts == 0)
    {
      int disable_interrupts
	= (lookup_attribute ("disinterrupt",
			     DECL_ATTRIBUTES (current_function_decl))
	   != NULL_TREE);
      cfun->machine->disable_interrupts = disable_interrupts ? 2 : 1;
    }
  return cfun->machine->disable_interrupts == 2;
}


/* Frame/Epilog/Prolog Related.  */

static bool
mep_reg_set_p (rtx reg, rtx insn)
{
  /* Similar to reg_set_p in rtlanal.c, but we ignore calls */
  if (INSN_P (insn))
    {
      if (FIND_REG_INC_NOTE (insn, reg))
	return true;
      insn = PATTERN (insn);
    }

  if (GET_CODE (insn) == SET
      && GET_CODE (XEXP (insn, 0)) == REG
      && GET_CODE (XEXP (insn, 1)) == REG
      && REGNO (XEXP (insn, 0)) == REGNO (XEXP (insn, 1)))
    return false;

  return set_of (reg, insn) != NULL_RTX;
}


#define MEP_SAVES_UNKNOWN 0
#define MEP_SAVES_YES 1
#define MEP_SAVES_MAYBE 2
#define MEP_SAVES_NO 3

static bool
mep_reg_set_in_function (int regno)
{
  rtx reg, insn;

  if (mep_interrupt_p () && df_regs_ever_live_p(regno))
    return true;

  if (regno == LP_REGNO && (profile_arc_flag > 0 || profile_flag > 0))
    return true;

  push_topmost_sequence ();
  insn = get_insns ();
  pop_topmost_sequence ();

  if (!insn)
    return false;

  reg = gen_rtx_REG (SImode, regno);

  for (insn = NEXT_INSN (insn); insn; insn = NEXT_INSN (insn))
    if (INSN_P (insn) && mep_reg_set_p (reg, insn))
      return true;
  return false;
}

static bool
mep_asm_without_operands_p (void)
{
  if (cfun->machine->asms_without_operands == 0)
    {
      rtx insn;

      push_topmost_sequence ();
      insn = get_insns ();
      pop_topmost_sequence ();

      cfun->machine->asms_without_operands = 1;
      while (insn)
	{
	  if (INSN_P (insn)
	      && GET_CODE (PATTERN (insn)) == ASM_INPUT)
	    {
	      cfun->machine->asms_without_operands = 2;
	      break;
	    }
	  insn = NEXT_INSN (insn);
	}

    }
  return cfun->machine->asms_without_operands == 2;
}

/* Interrupt functions save/restore every call-preserved register, and
   any call-used register it uses (or all if it calls any function,
   since they may get clobbered there too).  Here we check to see
   which call-used registers need saving.  */

#define IVC2_ISAVED_REG(r) (TARGET_IVC2 \
			   && (r == FIRST_CCR_REGNO + 1 \
			       || (r >= FIRST_CCR_REGNO + 8 && r <= FIRST_CCR_REGNO + 11) \
			       || (r >= FIRST_CCR_REGNO + 16 && r <= FIRST_CCR_REGNO + 31)))

static bool
mep_interrupt_saved_reg (int r)
{
  if (!mep_interrupt_p ())
    return false;
  if (r == REGSAVE_CONTROL_TEMP
      || (TARGET_64BIT_CR_REGS && TARGET_COP && r == REGSAVE_CONTROL_TEMP+1))
    return true;
  if (mep_asm_without_operands_p ()
      && (!fixed_regs[r]
	  || (r == RPB_REGNO || r == RPE_REGNO || r == RPC_REGNO || r == LP_REGNO)
	  || IVC2_ISAVED_REG (r)))
    return true;
  if (!current_function_is_leaf)
    /* Function calls mean we need to save $lp.  */
    if (r == LP_REGNO || IVC2_ISAVED_REG (r))
      return true;
  if (!current_function_is_leaf || cfun->machine->doloop_tags > 0)
    /* The interrupt handler might use these registers for repeat blocks,
       or it might call a function that does so.  */
    if (r == RPB_REGNO || r == RPE_REGNO || r == RPC_REGNO)
      return true;
  if (current_function_is_leaf && call_used_regs[r] && !df_regs_ever_live_p(r))
    return false;
  /* Functions we call might clobber these.  */
  if (call_used_regs[r] && !fixed_regs[r])
    return true;
  /* Additional registers that need to be saved for IVC2.  */
  if (IVC2_ISAVED_REG (r))
    return true;

  return false;
}

static bool
mep_call_saves_register (int r)
{
  if (! cfun->machine->frame_locked)
    {
      int rv = MEP_SAVES_NO;

      if (cfun->machine->reg_save_slot[r])
  	rv = MEP_SAVES_YES;
      else if (r == LP_REGNO && (profile_arc_flag > 0 || profile_flag > 0))
	rv = MEP_SAVES_YES;
      else if (r == FRAME_POINTER_REGNUM && frame_pointer_needed)
	rv = MEP_SAVES_YES;
      else if ((!call_used_regs[r] || r == LP_REGNO) && df_regs_ever_live_p(r))
	rv = MEP_SAVES_YES;
      else if (crtl->calls_eh_return && (r == 10 || r == 11))
	/* We need these to have stack slots so that they can be set during
	   unwinding.  */
	rv = MEP_SAVES_YES;
      else if (mep_interrupt_saved_reg (r))
	rv = MEP_SAVES_YES;
      cfun->machine->reg_saved[r] = rv;
    }
  return cfun->machine->reg_saved[r] == MEP_SAVES_YES;
}

/* Return true if epilogue uses register REGNO.  */

bool
mep_epilogue_uses (int regno)
{
  /* Since $lp is a call-saved register, the generic code will normally
     mark it used in the epilogue if it needs to be saved and restored.
     However, when profiling is enabled, the profiling code will implicitly
     clobber $11.  This case has to be handled specially both here and in
     mep_call_saves_register.  */
  if (regno == LP_REGNO && (profile_arc_flag > 0 || profile_flag > 0))
    return true;
  /* Interrupt functions save/restore pretty much everything.  */
  return (reload_completed && mep_interrupt_saved_reg (regno));
}

static int
mep_reg_size (int regno)
{
  if (CR_REGNO_P (regno) && TARGET_64BIT_CR_REGS)
    return 8;
  return 4;
}

/* Worker function for TARGET_CAN_ELIMINATE.  */

bool
mep_can_eliminate (const int from, const int to)
{
  return  (from == ARG_POINTER_REGNUM && to == STACK_POINTER_REGNUM
           ? ! frame_pointer_needed
           : true);
}

int
mep_elimination_offset (int from, int to)
{
  int reg_save_size;
  int i;
  int frame_size = get_frame_size () + crtl->outgoing_args_size;
  int total_size;

  if (!cfun->machine->frame_locked)
    memset (cfun->machine->reg_saved, 0, sizeof (cfun->machine->reg_saved));

  /* We don't count arg_regs_to_save in the arg pointer offset, because
     gcc thinks the arg pointer has moved along with the saved regs.
     However, we do count it when we adjust $sp in the prologue.  */
  reg_save_size = 0;
  for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
    if (mep_call_saves_register (i))
      reg_save_size += mep_reg_size (i);

  if (reg_save_size % 8)
    cfun->machine->regsave_filler = 8 - (reg_save_size % 8);
  else
    cfun->machine->regsave_filler = 0;

  /* This is what our total stack adjustment looks like.  */
  total_size = (reg_save_size + frame_size + cfun->machine->regsave_filler);

  if (total_size % 8)
    cfun->machine->frame_filler = 8 - (total_size % 8);
  else
    cfun->machine->frame_filler = 0;


  if (from == ARG_POINTER_REGNUM && to == FRAME_POINTER_REGNUM)
    return reg_save_size + cfun->machine->regsave_filler;

  if (from == FRAME_POINTER_REGNUM && to == STACK_POINTER_REGNUM)
    return cfun->machine->frame_filler + frame_size;

  if (from == ARG_POINTER_REGNUM && to == STACK_POINTER_REGNUM)
    return reg_save_size + cfun->machine->regsave_filler + cfun->machine->frame_filler + frame_size;

  gcc_unreachable ();
}

static rtx
F (rtx x)
{
  RTX_FRAME_RELATED_P (x) = 1;
  return x;
}

/* Since the prologue/epilogue code is generated after optimization,
   we can't rely on gcc to split constants for us.  So, this code
   captures all the ways to add a constant to a register in one logic
   chunk, including optimizing away insns we just don't need.  This
   makes the prolog/epilog code easier to follow.  */
static void
add_constant (int dest, int src, int value, int mark_frame)
{
  rtx insn;
  int hi, lo;

  if (src == dest && value == 0)
    return;

  if (value == 0)
    {
      insn = emit_move_insn (gen_rtx_REG (SImode, dest),
			     gen_rtx_REG (SImode, src));
      if (mark_frame)
	RTX_FRAME_RELATED_P(insn) = 1;
      return;
    }

  if (value >= -32768 && value <= 32767)
    {
      insn = emit_insn (gen_addsi3 (gen_rtx_REG (SImode, dest),
				    gen_rtx_REG (SImode, src),
				    GEN_INT (value)));
      if (mark_frame)
	RTX_FRAME_RELATED_P(insn) = 1;
      return;
    }

  /* Big constant, need to use a temp register.  We use
     REGSAVE_CONTROL_TEMP because it's call clobberable (the reg save
     area is always small enough to directly add to).  */

  hi = trunc_int_for_mode (value & 0xffff0000, SImode);
  lo = value & 0xffff;

  insn = emit_move_insn (gen_rtx_REG (SImode, REGSAVE_CONTROL_TEMP),
			 GEN_INT (hi));

  if (lo)
    {
      insn = emit_insn (gen_iorsi3 (gen_rtx_REG (SImode, REGSAVE_CONTROL_TEMP),
				    gen_rtx_REG (SImode, REGSAVE_CONTROL_TEMP),
				    GEN_INT (lo)));
    }

  insn = emit_insn (gen_addsi3 (gen_rtx_REG (SImode, dest),
				gen_rtx_REG (SImode, src),
				gen_rtx_REG (SImode, REGSAVE_CONTROL_TEMP)));
  if (mark_frame)
    {
      RTX_FRAME_RELATED_P(insn) = 1;
      add_reg_note (insn, REG_FRAME_RELATED_EXPR,
		    gen_rtx_SET (SImode,
				 gen_rtx_REG (SImode, dest),
				 gen_rtx_PLUS (SImode,
					       gen_rtx_REG (SImode, dest),
					       GEN_INT (value))));
    }
}

/* Move SRC to DEST.  Mark the move as being potentially dead if
   MAYBE_DEAD_P.  */

static rtx
maybe_dead_move (rtx dest, rtx src, bool ATTRIBUTE_UNUSED maybe_dead_p)
{
  rtx insn = emit_move_insn (dest, src);
#if 0
  if (maybe_dead_p)
    REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD, const0_rtx, NULL);
#endif
  return insn;
}

/* Used for interrupt functions, which can't assume that $tp and $gp
   contain the correct pointers.  */

static void
mep_reload_pointer (int regno, const char *symbol)
{
  rtx reg, sym;

  if (!df_regs_ever_live_p(regno) && current_function_is_leaf)
    return;

  reg = gen_rtx_REG (SImode, regno);
  sym = gen_rtx_SYMBOL_REF (SImode, symbol);
  emit_insn (gen_movsi_topsym_s (reg, sym));
  emit_insn (gen_movsi_botsym_s (reg, reg, sym));
}

/* Assign save slots for any register not already saved.  DImode
   registers go at the end of the reg save area; the rest go at the
   beginning.  This is for alignment purposes.  Returns true if a frame
   is really needed.  */
static bool
mep_assign_save_slots (int reg_save_size)
{
  bool really_need_stack_frame = false;
  int di_ofs = 0;
  int i;

  for (i=0; i<FIRST_PSEUDO_REGISTER; i++)
    if (mep_call_saves_register(i))
      {
	int regsize = mep_reg_size (i);

	if ((i != TP_REGNO && i != GP_REGNO && i != LP_REGNO)
	    || mep_reg_set_in_function (i))
	  really_need_stack_frame = true;

	if (cfun->machine->reg_save_slot[i])
	  continue;

	if (regsize < 8)
	  {
	    cfun->machine->reg_save_size += regsize;
	    cfun->machine->reg_save_slot[i] = cfun->machine->reg_save_size;
	  }
	else
	  {
	    cfun->machine->reg_save_slot[i] = reg_save_size - di_ofs;
	    di_ofs += 8;
	  }
      }
  cfun->machine->frame_locked = 1;
  return really_need_stack_frame;
}

void
mep_expand_prologue (void)
{
  int i, rss, sp_offset = 0;
  int reg_save_size;
  int frame_size;
  int really_need_stack_frame;

  /* We must not allow register renaming in interrupt functions,
     because that invalidates the correctness of the set of call-used
     registers we're going to save/restore.  */
  mep_set_leaf_registers (mep_interrupt_p () ? 0 : 1);

  if (mep_disinterrupt_p ())
    emit_insn (gen_mep_disable_int ());

  cfun->machine->mep_frame_pointer_needed = frame_pointer_needed;

  reg_save_size = mep_elimination_offset (ARG_POINTER_REGNUM, FRAME_POINTER_REGNUM);
  frame_size = mep_elimination_offset (FRAME_POINTER_REGNUM, STACK_POINTER_REGNUM);
  really_need_stack_frame = frame_size;

  really_need_stack_frame |= mep_assign_save_slots (reg_save_size);

  sp_offset = reg_save_size;
  if (sp_offset + frame_size < 128)
    sp_offset += frame_size ;

  add_constant (SP_REGNO, SP_REGNO, -sp_offset, 1);

  for (i=0; i<FIRST_PSEUDO_REGISTER; i++)
    if (mep_call_saves_register(i))
      {
	rtx mem;
	bool maybe_dead_p;
	enum machine_mode rmode;

	rss = cfun->machine->reg_save_slot[i];

  	if ((i == TP_REGNO || i == GP_REGNO || i == LP_REGNO)
	    && (!mep_reg_set_in_function (i)
		&& !mep_interrupt_p ()))
	  continue;

	if (mep_reg_size (i) == 8)
	  rmode = DImode;
	else
	  rmode = SImode;

	/* If there is a pseudo associated with this register's initial value,
	   reload might have already spilt it to the stack slot suggested by
	   ALLOCATE_INITIAL_VALUE.  The moves emitted here can then be safely
	   deleted as dead.  */
	mem = gen_rtx_MEM (rmode,
			   plus_constant (stack_pointer_rtx, sp_offset - rss));
	maybe_dead_p = rtx_equal_p (mem, has_hard_reg_initial_val (rmode, i));

	if (GR_REGNO_P (i) || LOADABLE_CR_REGNO_P (i))
	  F(maybe_dead_move (mem, gen_rtx_REG (rmode, i), maybe_dead_p));
	else if (rmode == DImode)
	  {
	    rtx insn;
	    int be = TARGET_BIG_ENDIAN ? 4 : 0;

	    mem = gen_rtx_MEM (SImode,
			       plus_constant (stack_pointer_rtx, sp_offset - rss + be));

	    maybe_dead_move (gen_rtx_REG (SImode, REGSAVE_CONTROL_TEMP),
			     gen_rtx_REG (SImode, i),
			     maybe_dead_p);
	    maybe_dead_move (gen_rtx_REG (SImode, REGSAVE_CONTROL_TEMP+1),
			     gen_rtx_ZERO_EXTRACT (SImode,
						   gen_rtx_REG (DImode, i),
						   GEN_INT (32),
						   GEN_INT (32)),
			     maybe_dead_p);
	    insn = maybe_dead_move (mem,
				    gen_rtx_REG (SImode, REGSAVE_CONTROL_TEMP),
				    maybe_dead_p);
	    RTX_FRAME_RELATED_P (insn) = 1;
	    
	    add_reg_note (insn, REG_FRAME_RELATED_EXPR,
			  gen_rtx_SET (VOIDmode,
				       copy_rtx (mem),
				       gen_rtx_REG (rmode, i)));
	    mem = gen_rtx_MEM (SImode,
			       plus_constant (stack_pointer_rtx, sp_offset - rss + (4-be)));
	    insn = maybe_dead_move (mem,
				    gen_rtx_REG (SImode, REGSAVE_CONTROL_TEMP+1),
				    maybe_dead_p);
	  }
	else
	  {
	    rtx insn;
	    maybe_dead_move (gen_rtx_REG (rmode, REGSAVE_CONTROL_TEMP),
			     gen_rtx_REG (rmode, i),
			     maybe_dead_p);
	    insn = maybe_dead_move (mem,
				    gen_rtx_REG (rmode, REGSAVE_CONTROL_TEMP),
				    maybe_dead_p);
	    RTX_FRAME_RELATED_P (insn) = 1;
	    
	    add_reg_note (insn, REG_FRAME_RELATED_EXPR,
			  gen_rtx_SET (VOIDmode,
				       copy_rtx (mem),
				       gen_rtx_REG (rmode, i)));
	  }
      }
  
  if (frame_pointer_needed)
    {
      /* We've already adjusted down by sp_offset.  Total $sp change
	 is reg_save_size + frame_size.  We want a net change here of
	 just reg_save_size.  */
      add_constant (FP_REGNO, SP_REGNO, sp_offset - reg_save_size, 1);
    }

  add_constant (SP_REGNO, SP_REGNO, sp_offset-(reg_save_size+frame_size), 1);

  if (mep_interrupt_p ())
    {
      mep_reload_pointer(GP_REGNO, "__sdabase");
      mep_reload_pointer(TP_REGNO, "__tpbase");
    }
}

static void
mep_start_function (FILE *file, HOST_WIDE_INT hwi_local)
{
  int local = hwi_local;
  int frame_size = local + crtl->outgoing_args_size;
  int reg_save_size;
  int ffill;
  int i, sp, skip;
  int sp_offset;
  int slot_map[FIRST_PSEUDO_REGISTER], si, sj;

  reg_save_size = mep_elimination_offset (ARG_POINTER_REGNUM, FRAME_POINTER_REGNUM);
  frame_size = mep_elimination_offset (FRAME_POINTER_REGNUM, STACK_POINTER_REGNUM);
  sp_offset = reg_save_size + frame_size;

  ffill = cfun->machine->frame_filler;

  if (cfun->machine->mep_frame_pointer_needed)
    reg_names[FP_REGNO] = "$fp";
  else
    reg_names[FP_REGNO] = "$8";

  if (sp_offset == 0)
    return;

  if (debug_info_level == DINFO_LEVEL_NONE)
    {
      fprintf (file, "\t# frame: %d", sp_offset);
      if (reg_save_size)
	fprintf (file, "   %d regs", reg_save_size);
      if (local)
	fprintf (file, "   %d locals", local);
      if (crtl->outgoing_args_size)
	fprintf (file, "   %d args", crtl->outgoing_args_size);
      fprintf (file, "\n");
      return;
    }

  fprintf (file, "\t#\n");
  fprintf (file, "\t# Initial Frame Information:\n");
  if (sp_offset || !frame_pointer_needed)
    fprintf (file, "\t# Entry   ---------- 0\n");

  /* Sort registers by save slots, so they're printed in the order
     they appear in memory, not the order they're saved in.  */
  for (si=0; si<FIRST_PSEUDO_REGISTER; si++)
    slot_map[si] = si;
  for (si=0; si<FIRST_PSEUDO_REGISTER-1; si++)
    for (sj=si+1; sj<FIRST_PSEUDO_REGISTER; sj++)
      if (cfun->machine->reg_save_slot[slot_map[si]]
	  > cfun->machine->reg_save_slot[slot_map[sj]])
	{
	  int t = slot_map[si];
	  slot_map[si] = slot_map[sj];
	  slot_map[sj] = t;
	}

  sp = 0;
  for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
    {
      int rsize;
      int r = slot_map[i];
      int rss = cfun->machine->reg_save_slot[r];

      if (!mep_call_saves_register (r))
	continue;

      if ((r == TP_REGNO || r == GP_REGNO || r == LP_REGNO)
	  && (!mep_reg_set_in_function (r)
	      && !mep_interrupt_p ()))
	continue;

      rsize = mep_reg_size(r);
      skip = rss - (sp+rsize);
      if (skip)
	fprintf (file, "\t#         %3d bytes for alignment\n", skip);
      fprintf (file, "\t#         %3d bytes for saved %-3s   %3d($sp)\n",
	       rsize, reg_names[r], sp_offset - rss);
      sp = rss;
    }

  skip = reg_save_size - sp;
  if (skip)
    fprintf (file, "\t#         %3d bytes for alignment\n", skip);

  if (frame_pointer_needed)
    fprintf (file, "\t# FP ---> ---------- %d (sp-%d)\n", reg_save_size, sp_offset-reg_save_size);
  if (local)
    fprintf (file, "\t#         %3d bytes for local vars\n", local);
  if (ffill)
    fprintf (file, "\t#         %3d bytes for alignment\n", ffill);
  if (crtl->outgoing_args_size)
    fprintf (file, "\t#         %3d bytes for outgoing args\n",
	     crtl->outgoing_args_size);
  fprintf (file, "\t# SP ---> ---------- %d\n", sp_offset);
  fprintf (file, "\t#\n");
}


static int mep_prevent_lp_restore = 0;
static int mep_sibcall_epilogue = 0;

void
mep_expand_epilogue (void)
{
  int i, sp_offset = 0;
  int reg_save_size = 0;
  int frame_size;
  int lp_temp = LP_REGNO, lp_slot = -1;
  int really_need_stack_frame = get_frame_size() + crtl->outgoing_args_size;
  int interrupt_handler = mep_interrupt_p ();

  if (profile_arc_flag == 2)
    emit_insn (gen_mep_bb_trace_ret ());

  reg_save_size = mep_elimination_offset (ARG_POINTER_REGNUM, FRAME_POINTER_REGNUM);
  frame_size = mep_elimination_offset (FRAME_POINTER_REGNUM, STACK_POINTER_REGNUM);

  really_need_stack_frame |= mep_assign_save_slots (reg_save_size);

  if (frame_pointer_needed)
    {
      /* If we have a frame pointer, we won't have a reliable stack
	 pointer (alloca, you know), so rebase SP from FP */
      emit_move_insn (gen_rtx_REG (SImode, SP_REGNO),
		      gen_rtx_REG (SImode, FP_REGNO));
      sp_offset = reg_save_size;
    }
  else
    {
      /* SP is right under our local variable space.  Adjust it if
	 needed.  */
      sp_offset = reg_save_size + frame_size;
      if (sp_offset >= 128)
	{
	  add_constant (SP_REGNO, SP_REGNO, frame_size, 0);
	  sp_offset -= frame_size;
	}
    }

  /* This is backwards so that we restore the control and coprocessor
     registers before the temporary registers we use to restore
     them.  */
  for (i=FIRST_PSEUDO_REGISTER-1; i>=1; i--)
    if (mep_call_saves_register (i))
      {
	enum machine_mode rmode;
	int rss = cfun->machine->reg_save_slot[i];

	if (mep_reg_size (i) == 8)
	  rmode = DImode;
	else
	  rmode = SImode;

	if ((i == TP_REGNO || i == GP_REGNO || i == LP_REGNO)
	    && !(mep_reg_set_in_function (i) || interrupt_handler))
	  continue;
	if (mep_prevent_lp_restore && i == LP_REGNO)
	  continue;
	if (!mep_prevent_lp_restore
	    && !interrupt_handler
	    && (i == 10 || i == 11))
	  continue;
  
	if (GR_REGNO_P (i) || LOADABLE_CR_REGNO_P (i))
	  emit_move_insn (gen_rtx_REG (rmode, i),
			  gen_rtx_MEM (rmode,
				       plus_constant (stack_pointer_rtx,
						      sp_offset-rss)));
	else
	  {
	    if (i == LP_REGNO && !mep_sibcall_epilogue && !interrupt_handler)
	      /* Defer this one so we can jump indirect rather than
		 copying the RA to $lp and "ret".  EH epilogues
		 automatically skip this anyway.  */
	      lp_slot = sp_offset-rss;
	    else
	      {
		emit_move_insn (gen_rtx_REG (rmode, REGSAVE_CONTROL_TEMP),
				gen_rtx_MEM (rmode,
					     plus_constant (stack_pointer_rtx,
							    sp_offset-rss)));
		emit_move_insn (gen_rtx_REG (rmode, i),
				gen_rtx_REG (rmode, REGSAVE_CONTROL_TEMP));
	      }
	  }
      }
  if (lp_slot != -1)
    {
      /* Restore this one last so we know it will be in the temp
	 register when we return by jumping indirectly via the temp.  */
      emit_move_insn (gen_rtx_REG (SImode, REGSAVE_CONTROL_TEMP),
		      gen_rtx_MEM (SImode,
				   plus_constant (stack_pointer_rtx,
						  lp_slot)));
      lp_temp = REGSAVE_CONTROL_TEMP;
    }


  add_constant (SP_REGNO, SP_REGNO, sp_offset, 0);

  if (crtl->calls_eh_return && mep_prevent_lp_restore)
    emit_insn (gen_addsi3 (gen_rtx_REG (SImode, SP_REGNO),
			   gen_rtx_REG (SImode, SP_REGNO),
			   cfun->machine->eh_stack_adjust));

  if (mep_sibcall_epilogue)
    return;

  if (mep_disinterrupt_p ())
    emit_insn (gen_mep_enable_int ());

  if (mep_prevent_lp_restore)
    {
      emit_jump_insn (gen_eh_return_internal ());
      emit_barrier ();
    }
  else if (interrupt_handler)
    emit_jump_insn (gen_mep_reti ());
  else
    emit_jump_insn (gen_return_internal (gen_rtx_REG (SImode, lp_temp)));
}

void
mep_expand_eh_return (rtx *operands)
{
  if (GET_CODE (operands[0]) != REG || REGNO (operands[0]) != LP_REGNO)
    {
      rtx ra = gen_rtx_REG (Pmode, LP_REGNO);
      emit_move_insn (ra, operands[0]);
      operands[0] = ra;
    }

  emit_insn (gen_eh_epilogue (operands[0]));
}

void
mep_emit_eh_epilogue (rtx *operands ATTRIBUTE_UNUSED)
{
  cfun->machine->eh_stack_adjust = gen_rtx_REG (Pmode, 0);
  mep_prevent_lp_restore = 1;
  mep_expand_epilogue ();
  mep_prevent_lp_restore = 0;
}

void
mep_expand_sibcall_epilogue (void)
{
  mep_sibcall_epilogue = 1;
  mep_expand_epilogue ();
  mep_sibcall_epilogue = 0;
}

static bool
mep_function_ok_for_sibcall (tree decl, tree exp ATTRIBUTE_UNUSED)
{
  if (decl == NULL)
    return false;

  if (mep_section_tag (DECL_RTL (decl)) == 'f')
    return false;

  /* Can't call to a sibcall from an interrupt or disinterrupt function.  */
  if (mep_interrupt_p () || mep_disinterrupt_p ())
    return false;

  return true;
}

rtx
mep_return_stackadj_rtx (void)
{
  return gen_rtx_REG (SImode, 10);
}

rtx
mep_return_handler_rtx (void)
{
  return gen_rtx_REG (SImode, LP_REGNO);
}

void
mep_function_profiler (FILE *file)
{
  /* Always right at the beginning of the function.  */
  fprintf (file, "\t# mep function profiler\n");
  fprintf (file, "\tadd\t$sp, -8\n");
  fprintf (file, "\tsw\t$0, ($sp)\n");
  fprintf (file, "\tldc\t$0, $lp\n");
  fprintf (file, "\tsw\t$0, 4($sp)\n");
  fprintf (file, "\tbsr\t__mep_mcount\n");
  fprintf (file, "\tlw\t$0, 4($sp)\n");
  fprintf (file, "\tstc\t$0, $lp\n");
  fprintf (file, "\tlw\t$0, ($sp)\n");
  fprintf (file, "\tadd\t$sp, 8\n\n");
}

const char *
mep_emit_bb_trace_ret (void)
{
  fprintf (asm_out_file, "\t# end of block profiling\n");
  fprintf (asm_out_file, "\tadd\t$sp, -8\n");
  fprintf (asm_out_file, "\tsw\t$0, ($sp)\n");
  fprintf (asm_out_file, "\tldc\t$0, $lp\n");
  fprintf (asm_out_file, "\tsw\t$0, 4($sp)\n");
  fprintf (asm_out_file, "\tbsr\t__bb_trace_ret\n");
  fprintf (asm_out_file, "\tlw\t$0, 4($sp)\n");
  fprintf (asm_out_file, "\tstc\t$0, $lp\n");
  fprintf (asm_out_file, "\tlw\t$0, ($sp)\n");
  fprintf (asm_out_file, "\tadd\t$sp, 8\n\n");
  return "";
}

#undef SAVE
#undef RESTORE

/* Operand Printing.  */

void
mep_print_operand_address (FILE *stream, rtx address)
{
  if (GET_CODE (address) == MEM)
    address = XEXP (address, 0);
  else
    /* cf: gcc.dg/asm-4.c.  */
    gcc_assert (GET_CODE (address) == REG);

  mep_print_operand (stream, address, 0);
}

static struct
{
  char code;
  const char *pattern;
  const char *format;
}
const conversions[] =
{
  { 0, "r", "0" },
  { 0, "m+ri", "3(2)" },
  { 0, "mr", "(1)" },
  { 0, "ms", "(1)" },
  { 0, "ml", "(1)" },
  { 0, "mLrs", "%lo(3)(2)" },
  { 0, "mLr+si", "%lo(4+5)(2)" },
  { 0, "m+ru2s", "%tpoff(5)(2)" },
  { 0, "m+ru3s", "%sdaoff(5)(2)" },
  { 0, "m+r+u2si", "%tpoff(6+7)(2)" },
  { 0, "m+ru2+si", "%tpoff(6+7)(2)" },
  { 0, "m+r+u3si", "%sdaoff(6+7)(2)" },
  { 0, "m+ru3+si", "%sdaoff(6+7)(2)" },
  { 0, "mi", "(1)" },
  { 0, "m+si", "(2+3)" },
  { 0, "m+li", "(2+3)" },
  { 0, "i", "0" },
  { 0, "s", "0" },
  { 0, "+si", "1+2" },
  { 0, "+u2si", "%tpoff(3+4)" },
  { 0, "+u3si", "%sdaoff(3+4)" },
  { 0, "l", "0" },
  { 'b', "i", "0" },
  { 'B', "i", "0" },
  { 'U', "i", "0" },
  { 'h', "i", "0" },
  { 'h', "Hs", "%hi(1)" },
  { 'I', "i", "0" },
  { 'I', "u2s", "%tpoff(2)" },
  { 'I', "u3s", "%sdaoff(2)" },
  { 'I', "+u2si", "%tpoff(3+4)" },
  { 'I', "+u3si", "%sdaoff(3+4)" },
  { 'J', "i", "0" },
  { 'P', "mr", "(1\\+),\\0" },
  { 'x', "i", "0" },
  { 0, 0, 0 }
};

static int
unique_bit_in (HOST_WIDE_INT i)
{
  switch (i & 0xff)
    {
    case 0x01: case 0xfe: return 0;
    case 0x02: case 0xfd: return 1;
    case 0x04: case 0xfb: return 2;
    case 0x08: case 0xf7: return 3;
    case 0x10: case 0x7f: return 4;
    case 0x20: case 0xbf: return 5;
    case 0x40: case 0xdf: return 6;
    case 0x80: case 0xef: return 7;
    default:
      gcc_unreachable ();
    }
}

static int
bit_size_for_clip (HOST_WIDE_INT i)
{
  int rv;

  for (rv = 0; rv < 31; rv ++)
    if (((HOST_WIDE_INT) 1 << rv) > i)
      return rv + 1;
  gcc_unreachable ();
}

/* Print an operand to a assembler instruction.  */

void
mep_print_operand (FILE *file, rtx x, int code)
{
  int i, j;
  const char *real_name;

  if (code == '<')
    {
      /* Print a mnemonic to do CR <- CR moves.  Find out which intrinsic
	 we're using, then skip over the "mep_" part of its name.  */
      const struct cgen_insn *insn;

      if (mep_get_move_insn (mep_cmov, &insn))
	fputs (cgen_intrinsics[insn->intrinsic] + 4, file);
      else
	mep_intrinsic_unavailable (mep_cmov);
      return;
    }
  if (code == 'L')
    {
      switch (GET_CODE (x))
	{
	case AND:
	  fputs ("clr", file);
	  return;
	case IOR:
	  fputs ("set", file);
	  return;
	case XOR:
	  fputs ("not", file);
	  return;
	default:
	  output_operand_lossage ("invalid %%L code");
	}
    }
  if (code == 'M')
    {
      /* Print the second operand of a CR <- CR move.  If we're using
	 a two-operand instruction (i.e., a real cmov), then just print
	 the operand normally.  If we're using a "reg, reg, immediate"
	 instruction such as caddi3, print the operand followed by a
	 zero field.  If we're using a three-register instruction,
	 print the operand twice.  */
      const struct cgen_insn *insn;

      mep_print_operand (file, x, 0);
      if (mep_get_move_insn (mep_cmov, &insn)
	  && insn_data[insn->icode].n_operands == 3)
	{
	  fputs (", ", file);
	  if (insn_data[insn->icode].operand[2].predicate (x, VOIDmode))
	    mep_print_operand (file, x, 0);
	  else
	    mep_print_operand (file, const0_rtx, 0);
	}
      return;
    }

  encode_pattern (x);
  for (i = 0; conversions[i].pattern; i++)
    if (conversions[i].code == code
	&& strcmp(conversions[i].pattern, pattern) == 0)
      {
	for (j = 0; conversions[i].format[j]; j++)
	  if (conversions[i].format[j] == '\\')
	    {
	      fputc (conversions[i].format[j+1], file);
	      j++;
	    }
	  else if (ISDIGIT(conversions[i].format[j]))
	    {
	      rtx r = patternr[conversions[i].format[j] - '0'];
	      switch (GET_CODE (r))
		{
		case REG:
		  fprintf (file, "%s", reg_names [REGNO (r)]);
		  break;
		case CONST_INT:
		  switch (code)
		    {
		    case 'b':
		      fprintf (file, "%d", unique_bit_in (INTVAL (r)));
		      break;
		    case 'B':
		      fprintf (file, "%d", bit_size_for_clip (INTVAL (r)));
		      break;
		    case 'h':
		      fprintf (file, "0x%x", ((int) INTVAL (r) >> 16) & 0xffff);
		      break;
		    case 'U':
		      fprintf (file, "%d", bit_size_for_clip (INTVAL (r)) - 1);
		      break;
		    case 'J':
		      fprintf (file, "0x%x", (int) INTVAL (r) & 0xffff);
		      break;
		    case 'x':
		      if (INTVAL (r) & ~(HOST_WIDE_INT)0xff
			  && !(INTVAL (r) & 0xff))
			fprintf (file, HOST_WIDE_INT_PRINT_HEX, INTVAL(r));
		      else
			fprintf (file, HOST_WIDE_INT_PRINT_DEC, INTVAL(r));
		      break;
		    case 'I':
		      if (INTVAL (r) & ~(HOST_WIDE_INT)0xff
			  && conversions[i].format[j+1] == 0)
			{
			  fprintf (file, HOST_WIDE_INT_PRINT_DEC, INTVAL (r));
			  fprintf (file, " # 0x%x", (int) INTVAL(r) & 0xffff);
			}
		      else
			fprintf (file, HOST_WIDE_INT_PRINT_DEC, INTVAL(r));
		      break;
		    default:
		      fprintf (file, HOST_WIDE_INT_PRINT_DEC, INTVAL(r));
		      break;
		    }
		  break;
		case CONST_DOUBLE:
		  fprintf(file, "[const_double 0x%lx]",
			  (unsigned long) CONST_DOUBLE_HIGH(r));
		  break;
		case SYMBOL_REF:
		  real_name = targetm.strip_name_encoding (XSTR (r, 0));
		  assemble_name (file, real_name);
		  break;
		case LABEL_REF:
		  output_asm_label (r);
		  break;
		default:
		  fprintf (stderr, "don't know how to print this operand:");
		  debug_rtx (r);
		  gcc_unreachable ();
		}
	    }
	  else
	    {
	      if (conversions[i].format[j] == '+'
		  && (!code || code == 'I')
		  && ISDIGIT (conversions[i].format[j+1])
		  && GET_CODE (patternr[conversions[i].format[j+1] - '0']) == CONST_INT
		  && INTVAL (patternr[conversions[i].format[j+1] - '0']) < 0)
		continue;
	      fputc(conversions[i].format[j], file);
	    }
	break;
      }
  if (!conversions[i].pattern)
    {
      error ("unconvertible operand %c %qs", code?code:'-', pattern);
      debug_rtx(x);
    }

  return;
}

void
mep_final_prescan_insn (rtx insn, rtx *operands ATTRIBUTE_UNUSED,
			int noperands ATTRIBUTE_UNUSED)
{
  /* Despite the fact that MeP is perfectly capable of branching and
     doing something else in the same bundle, gcc does jump
     optimization *after* scheduling, so we cannot trust the bundling
     flags on jump instructions.  */
  if (GET_MODE (insn) == BImode
      && get_attr_slots (insn) != SLOTS_CORE)
    fputc ('+', asm_out_file);
}

/* Function args in registers.  */

static void
mep_setup_incoming_varargs (CUMULATIVE_ARGS *cum,
			    enum machine_mode mode ATTRIBUTE_UNUSED,
			    tree type ATTRIBUTE_UNUSED, int *pretend_size,
			    int second_time ATTRIBUTE_UNUSED)
{
  int nsave = 4 - (cum->nregs + 1);

  if (nsave > 0)
    cfun->machine->arg_regs_to_save = nsave;
  *pretend_size = nsave * 4;
}

static int
bytesize (const_tree type, enum machine_mode mode)
{
  if (mode == BLKmode)
    return int_size_in_bytes (type);
  return GET_MODE_SIZE (mode);
}

static rtx
mep_expand_builtin_saveregs (void)
{
  int bufsize, i, ns;
  rtx regbuf;

  ns = cfun->machine->arg_regs_to_save;
  if (TARGET_IVC2)
    {
      bufsize = 8 * ((ns + 1) / 2) + 8 * ns;
      regbuf = assign_stack_local (SImode, bufsize, 64);
    }
  else
    {
      bufsize = ns * 4;
      regbuf = assign_stack_local (SImode, bufsize, 32);
    }

  move_block_from_reg (5-ns, regbuf, ns);

  if (TARGET_IVC2)
    {
      rtx tmp = gen_rtx_MEM (DImode, XEXP (regbuf, 0));
      int ofs = 8 * ((ns+1)/2);

      for (i=0; i<ns; i++)
	{
	  int rn = (4-ns) + i + 49;
	  rtx ptr;

	  ptr = offset_address (tmp, GEN_INT (ofs), 2);
	  emit_move_insn (ptr, gen_rtx_REG (DImode, rn));
	  ofs += 8;
	}
    }
  return XEXP (regbuf, 0);
}

#define VECTOR_TYPE_P(t) (TREE_CODE(t) == VECTOR_TYPE)

static tree
mep_build_builtin_va_list (void)
{
  tree f_next_gp, f_next_gp_limit, f_next_cop, f_next_stack;
  tree record;


  record = (*lang_hooks.types.make_type) (RECORD_TYPE);

  f_next_gp = build_decl (BUILTINS_LOCATION, FIELD_DECL,
			  get_identifier ("__va_next_gp"), ptr_type_node);
  f_next_gp_limit = build_decl (BUILTINS_LOCATION, FIELD_DECL,
				get_identifier ("__va_next_gp_limit"),
				ptr_type_node);
  f_next_cop = build_decl (BUILTINS_LOCATION, FIELD_DECL, get_identifier ("__va_next_cop"),
			   ptr_type_node);
  f_next_stack = build_decl (BUILTINS_LOCATION, FIELD_DECL, get_identifier ("__va_next_stack"),
			     ptr_type_node);

  DECL_FIELD_CONTEXT (f_next_gp) = record;
  DECL_FIELD_CONTEXT (f_next_gp_limit) = record;
  DECL_FIELD_CONTEXT (f_next_cop) = record;
  DECL_FIELD_CONTEXT (f_next_stack) = record;

  TYPE_FIELDS (record) = f_next_gp;
  DECL_CHAIN (f_next_gp) = f_next_gp_limit;
  DECL_CHAIN (f_next_gp_limit) = f_next_cop;
  DECL_CHAIN (f_next_cop) = f_next_stack;

  layout_type (record);

  return record;
}

static void
mep_expand_va_start (tree valist, rtx nextarg)
{
  tree f_next_gp, f_next_gp_limit, f_next_cop, f_next_stack;
  tree next_gp, next_gp_limit, next_cop, next_stack;
  tree t, u;
  int ns;

  ns = cfun->machine->arg_regs_to_save;

  f_next_gp = TYPE_FIELDS (va_list_type_node);
  f_next_gp_limit = DECL_CHAIN (f_next_gp);
  f_next_cop = DECL_CHAIN (f_next_gp_limit);
  f_next_stack = DECL_CHAIN (f_next_cop);

  next_gp = build3 (COMPONENT_REF, TREE_TYPE (f_next_gp), valist, f_next_gp,
		    NULL_TREE);
  next_gp_limit = build3 (COMPONENT_REF, TREE_TYPE (f_next_gp_limit),
			  valist, f_next_gp_limit, NULL_TREE);
  next_cop = build3 (COMPONENT_REF, TREE_TYPE (f_next_cop), valist, f_next_cop,
		     NULL_TREE);
  next_stack = build3 (COMPONENT_REF, TREE_TYPE (f_next_stack),
		       valist, f_next_stack, NULL_TREE);

  /* va_list.next_gp = expand_builtin_saveregs (); */
  u = make_tree (sizetype, expand_builtin_saveregs ());
  u = fold_convert (ptr_type_node, u);
  t = build2 (MODIFY_EXPR, ptr_type_node, next_gp, u);
  TREE_SIDE_EFFECTS (t) = 1;
  expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);

  /* va_list.next_gp_limit = va_list.next_gp + 4 * ns; */
  u = fold_build2 (POINTER_PLUS_EXPR, ptr_type_node, u,
		   size_int (4 * ns));
  t = build2 (MODIFY_EXPR, ptr_type_node, next_gp_limit, u);
  TREE_SIDE_EFFECTS (t) = 1;
  expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);

  u = fold_build2 (POINTER_PLUS_EXPR, ptr_type_node, u,
		   size_int (8 * ((ns+1)/2)));
  /* va_list.next_cop = ROUND_UP(va_list.next_gp_limit,8); */
  t = build2 (MODIFY_EXPR, ptr_type_node, next_cop, u);
  TREE_SIDE_EFFECTS (t) = 1;
  expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);

  /* va_list.next_stack = nextarg; */
  u = make_tree (ptr_type_node, nextarg);
  t = build2 (MODIFY_EXPR, ptr_type_node, next_stack, u);
  TREE_SIDE_EFFECTS (t) = 1;
  expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
}

static tree
mep_gimplify_va_arg_expr (tree valist, tree type,
			  gimple_seq *pre_p,
			  gimple_seq *post_p ATTRIBUTE_UNUSED)
{
  HOST_WIDE_INT size, rsize;
  bool by_reference, ivc2_vec;
  tree f_next_gp, f_next_gp_limit, f_next_cop, f_next_stack;
  tree next_gp, next_gp_limit, next_cop, next_stack;
  tree label_sover, label_selse;
  tree tmp, res_addr;

  ivc2_vec = TARGET_IVC2 && VECTOR_TYPE_P (type);

  size = int_size_in_bytes (type);
  by_reference = (size > (ivc2_vec ? 8 : 4)) || (size <= 0);

  if (by_reference)
    {
      type = build_pointer_type (type);
      size = 4;
    }
  rsize = (size + UNITS_PER_WORD - 1) & -UNITS_PER_WORD;

  f_next_gp = TYPE_FIELDS (va_list_type_node);
  f_next_gp_limit = DECL_CHAIN (f_next_gp);
  f_next_cop = DECL_CHAIN (f_next_gp_limit);
  f_next_stack = DECL_CHAIN (f_next_cop);

  next_gp = build3 (COMPONENT_REF, TREE_TYPE (f_next_gp), valist, f_next_gp,
		    NULL_TREE);
  next_gp_limit = build3 (COMPONENT_REF, TREE_TYPE (f_next_gp_limit),
			  valist, f_next_gp_limit, NULL_TREE);
  next_cop = build3 (COMPONENT_REF, TREE_TYPE (f_next_cop), valist, f_next_cop,
		     NULL_TREE);
  next_stack = build3 (COMPONENT_REF, TREE_TYPE (f_next_stack),
		       valist, f_next_stack, NULL_TREE);

  /* if f_next_gp < f_next_gp_limit
       IF (VECTOR_P && IVC2)
         val = *f_next_cop;
       ELSE
         val = *f_next_gp;
       f_next_gp += 4;
       f_next_cop += 8;
     else
       label_selse:
       val = *f_next_stack;
       f_next_stack += rsize;
     label_sover:
  */

  label_sover = create_artificial_label (UNKNOWN_LOCATION);
  label_selse = create_artificial_label (UNKNOWN_LOCATION);
  res_addr = create_tmp_var (ptr_type_node, NULL);

  tmp = build2 (GE_EXPR, boolean_type_node, next_gp,
		unshare_expr (next_gp_limit));
  tmp = build3 (COND_EXPR, void_type_node, tmp,
		build1 (GOTO_EXPR, void_type_node,
			unshare_expr (label_selse)),
		NULL_TREE);
  gimplify_and_add (tmp, pre_p);

  if (ivc2_vec)
    {
      tmp = build2 (MODIFY_EXPR, void_type_node, res_addr, next_cop);
      gimplify_and_add (tmp, pre_p);
    }
  else
    {
      tmp = build2 (MODIFY_EXPR, void_type_node, res_addr, next_gp);
      gimplify_and_add (tmp, pre_p);
    }

  tmp = build2 (POINTER_PLUS_EXPR, ptr_type_node,
		unshare_expr (next_gp), size_int (4));
  gimplify_assign (unshare_expr (next_gp), tmp, pre_p);

  tmp = build2 (POINTER_PLUS_EXPR, ptr_type_node,
		unshare_expr (next_cop), size_int (8));
  gimplify_assign (unshare_expr (next_cop), tmp, pre_p);

  tmp = build1 (GOTO_EXPR, void_type_node, unshare_expr (label_sover));
  gimplify_and_add (tmp, pre_p);

  /* - - */

  tmp = build1 (LABEL_EXPR, void_type_node, unshare_expr (label_selse));
  gimplify_and_add (tmp, pre_p);

  tmp = build2 (MODIFY_EXPR, void_type_node, res_addr, unshare_expr (next_stack));
  gimplify_and_add (tmp, pre_p);

  tmp = build2 (POINTER_PLUS_EXPR, ptr_type_node,
		unshare_expr (next_stack), size_int (rsize));
  gimplify_assign (unshare_expr (next_stack), tmp, pre_p);

  /* - - */

  tmp = build1 (LABEL_EXPR, void_type_node, unshare_expr (label_sover));
  gimplify_and_add (tmp, pre_p);

  res_addr = fold_convert (build_pointer_type (type), res_addr);

  if (by_reference)
    res_addr = build_va_arg_indirect_ref (res_addr);

  return build_va_arg_indirect_ref (res_addr);
}

void
mep_init_cumulative_args (CUMULATIVE_ARGS *pcum, tree fntype,
			  rtx libname ATTRIBUTE_UNUSED,
			  tree fndecl ATTRIBUTE_UNUSED)
{
  pcum->nregs = 0;

  if (fntype && lookup_attribute ("vliw", TYPE_ATTRIBUTES (fntype)))
    pcum->vliw = 1;
  else
    pcum->vliw = 0;
}

/* The ABI is thus: Arguments are in $1, $2, $3, $4, stack.  Arguments
   larger than 4 bytes are passed indirectly.  Return value in 0,
   unless bigger than 4 bytes, then the caller passes a pointer as the
   first arg.  For varargs, we copy $1..$4 to the stack.  */

static rtx
mep_function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode,
		  const_tree type ATTRIBUTE_UNUSED,
		  bool named ATTRIBUTE_UNUSED)
{
  /* VOIDmode is a signal for the backend to pass data to the call
     expander via the second operand to the call pattern.  We use
     this to determine whether to use "jsr" or "jsrv".  */
  if (mode == VOIDmode)
    return GEN_INT (cum->vliw);

  /* If we havn't run out of argument registers, return the next.  */
  if (cum->nregs < 4)
    {
      if (type && TARGET_IVC2 && VECTOR_TYPE_P (type))
	return gen_rtx_REG (mode, cum->nregs + 49);
      else
	return gen_rtx_REG (mode, cum->nregs + 1);
    }

  /* Otherwise the argument goes on the stack.  */
  return NULL_RTX;
}

static bool
mep_pass_by_reference (CUMULATIVE_ARGS * cum ATTRIBUTE_UNUSED,
		       enum machine_mode mode,
		       const_tree        type,
		       bool              named ATTRIBUTE_UNUSED)
{
  int size = bytesize (type, mode);

  /* This is non-obvious, but yes, large values passed after we've run
     out of registers are *still* passed by reference - we put the
     address of the parameter on the stack, as well as putting the
     parameter itself elsewhere on the stack.  */

  if (size <= 0 || size > 8)
    return true;
  if (size <= 4)
    return false;
  if (TARGET_IVC2 && cum->nregs < 4 && type != NULL_TREE && VECTOR_TYPE_P (type))
    return false;
  return true;
}

static void
mep_function_arg_advance (CUMULATIVE_ARGS *pcum,
			  enum machine_mode mode ATTRIBUTE_UNUSED,
			  const_tree type ATTRIBUTE_UNUSED,
			  bool named ATTRIBUTE_UNUSED)
{
  pcum->nregs += 1;
}

bool
mep_return_in_memory (const_tree type, const_tree decl ATTRIBUTE_UNUSED)
{
  int size = bytesize (type, BLKmode);
  if (TARGET_IVC2 && VECTOR_TYPE_P (type))
    return size > 0 && size <= 8 ? 0 : 1;
  return size > 0 && size <= 4 ? 0 : 1;
}

static bool
mep_narrow_volatile_bitfield (void)
{
  return true;
  return false;
}

/* Implement FUNCTION_VALUE.  All values are returned in $0.  */

rtx
mep_function_value (const_tree type, const_tree func ATTRIBUTE_UNUSED)
{
  if (TARGET_IVC2 && VECTOR_TYPE_P (type))
    return gen_rtx_REG (TYPE_MODE (type), 48);
  return gen_rtx_REG (TYPE_MODE (type), RETURN_VALUE_REGNUM);
}

/* Implement LIBCALL_VALUE, using the same rules as mep_function_value.  */

rtx
mep_libcall_value (enum machine_mode mode)
{
  return gen_rtx_REG (mode, RETURN_VALUE_REGNUM);
}

/* Handle pipeline hazards.  */

typedef enum { op_none, op_stc, op_fsft, op_ret } op_num;
static const char *opnames[] = { "", "stc", "fsft", "ret" };

static int prev_opcode = 0;

/* This isn't as optimal as it could be, because we don't know what
   control register the STC opcode is storing in.  We only need to add
   the nop if it's the relevent register, but we add it for irrelevent
   registers also.  */

void
mep_asm_output_opcode (FILE *file, const char *ptr)
{
  int this_opcode = op_none;
  const char *hazard = 0;

  switch (*ptr)
    {
    case 'f':
      if (strncmp (ptr, "fsft", 4) == 0 && !ISGRAPH (ptr[4]))
	this_opcode = op_fsft;
      break;
    case 'r':
      if (strncmp (ptr, "ret", 3) == 0 && !ISGRAPH (ptr[3]))
	this_opcode = op_ret;
      break;
    case 's':
      if (strncmp (ptr, "stc", 3) == 0 && !ISGRAPH (ptr[3]))
	this_opcode = op_stc;
      break;
    }

  if (prev_opcode == op_stc && this_opcode == op_fsft)
    hazard = "nop";
  if (prev_opcode == op_stc && this_opcode == op_ret)
    hazard = "nop";

  if (hazard)
    fprintf(file, "%s\t# %s-%s hazard\n\t",
	    hazard, opnames[prev_opcode], opnames[this_opcode]);

  prev_opcode = this_opcode;
}

/* Handle attributes.  */

static tree
mep_validate_based_tiny (tree *node, tree name, tree args,
			 int flags ATTRIBUTE_UNUSED, bool *no_add)
{
  if (TREE_CODE (*node) != VAR_DECL
      && TREE_CODE (*node) != POINTER_TYPE
      && TREE_CODE (*node) != TYPE_DECL)
    {
      warning (0, "%qE attribute only applies to variables", name);
      *no_add = true;
    }
  else if (args == NULL_TREE && TREE_CODE (*node) == VAR_DECL)
    {
      if (! (TREE_PUBLIC (*node) || TREE_STATIC (*node)))
	{
	  warning (0, "address region attributes not allowed with auto storage class");
	  *no_add = true;
	}
      /* Ignore storage attribute of pointed to variable: char __far * x;  */
      if (TREE_TYPE (*node) && TREE_CODE (TREE_TYPE (*node)) == POINTER_TYPE)
	{
	  warning (0, "address region attributes on pointed-to types ignored");
	  *no_add = true;
	}
    }
  
  return NULL_TREE;
}

static int
mep_multiple_address_regions (tree list, bool check_section_attr)
{
  tree a;
  int count_sections = 0;
  int section_attr_count = 0;

  for (a = list; a; a = TREE_CHAIN (a))
    {
      if (is_attribute_p ("based", TREE_PURPOSE (a))
	  || is_attribute_p ("tiny", TREE_PURPOSE (a))
	  || is_attribute_p ("near", TREE_PURPOSE (a))
	  || is_attribute_p ("far", TREE_PURPOSE (a))
	  || is_attribute_p ("io", TREE_PURPOSE (a)))
	count_sections ++;
      if (check_section_attr)
	section_attr_count += is_attribute_p ("section", TREE_PURPOSE (a));
    }
	
  if (check_section_attr)
    return section_attr_count;
  else
    return count_sections;
}

#define MEP_ATTRIBUTES(decl) \
  (TYPE_P (decl)) ? TYPE_ATTRIBUTES (decl) \
                : DECL_ATTRIBUTES (decl) \
                  ? (DECL_ATTRIBUTES (decl)) \
		  : TYPE_ATTRIBUTES (TREE_TYPE (decl))

static tree
mep_validate_near_far (tree *node, tree name, tree args,
		       int flags ATTRIBUTE_UNUSED, bool *no_add)
{
  if (TREE_CODE (*node) != VAR_DECL
      && TREE_CODE (*node) != FUNCTION_DECL
      && TREE_CODE (*node) != METHOD_TYPE
      && TREE_CODE (*node) != POINTER_TYPE
      && TREE_CODE (*node) != TYPE_DECL)
    {
      warning (0, "%qE attribute only applies to variables and functions",
	       name);
      *no_add = true;
    }
  else if (args == NULL_TREE && TREE_CODE (*node) == VAR_DECL)
    {
      if (! (TREE_PUBLIC (*node) || TREE_STATIC (*node)))
	{
	  warning (0, "address region attributes not allowed with auto storage class");
	  *no_add = true;
	}
      /* Ignore storage attribute of pointed to variable: char __far * x;  */
      if (TREE_TYPE (*node) && TREE_CODE (TREE_TYPE (*node)) == POINTER_TYPE)
	{
	  warning (0, "address region attributes on pointed-to types ignored");
	  *no_add = true;
	}
    }
  else if (mep_multiple_address_regions (MEP_ATTRIBUTES (*node), false) > 0)
    {
      warning (0, "duplicate address region attribute %qE in declaration of %qE on line %d",
	       name, DECL_NAME (*node), DECL_SOURCE_LINE (*node));
      DECL_ATTRIBUTES (*node) = NULL_TREE;
    }
  return NULL_TREE;
}

static tree
mep_validate_disinterrupt (tree *node, tree name, tree args ATTRIBUTE_UNUSED,
			   int flags ATTRIBUTE_UNUSED, bool *no_add)
{
  if (TREE_CODE (*node) != FUNCTION_DECL
      && TREE_CODE (*node) != METHOD_TYPE)
    {
      warning (0, "%qE attribute only applies to functions", name);
      *no_add = true;
    }
  return NULL_TREE;
}

static tree
mep_validate_interrupt (tree *node, tree name, tree args ATTRIBUTE_UNUSED,
			int flags ATTRIBUTE_UNUSED, bool *no_add)
{
  tree function_type;

  if (TREE_CODE (*node) != FUNCTION_DECL)
    {
      warning (0, "%qE attribute only applies to functions", name);
      *no_add = true;
      return NULL_TREE;
    }

  if (DECL_DECLARED_INLINE_P (*node))
    error ("cannot inline interrupt function %qE", DECL_NAME (*node));
  DECL_UNINLINABLE (*node) = 1;

  function_type = TREE_TYPE (*node);

  if (TREE_TYPE (function_type) != void_type_node)
    error ("interrupt function must have return type of void");

  if (prototype_p (function_type)
      && (TREE_VALUE (TYPE_ARG_TYPES (function_type)) != void_type_node
	  || TREE_CHAIN (TYPE_ARG_TYPES (function_type)) != NULL_TREE))
    error ("interrupt function must have no arguments");

  return NULL_TREE;
}

static tree
mep_validate_io_cb (tree *node, tree name, tree args,
		    int flags ATTRIBUTE_UNUSED, bool *no_add)
{
  if (TREE_CODE (*node) != VAR_DECL)
    {
      warning (0, "%qE attribute only applies to variables", name);
      *no_add = true;
    }

  if (args != NULL_TREE)
    {
      if (TREE_CODE (TREE_VALUE (args)) == NON_LVALUE_EXPR)
	TREE_VALUE (args) = TREE_OPERAND (TREE_VALUE (args), 0);
      if (TREE_CODE (TREE_VALUE (args)) != INTEGER_CST)
	{
	  warning (0, "%qE attribute allows only an integer constant argument",
		   name);
	  *no_add = true;
	}
    }

  if (*no_add == false && !TARGET_IO_NO_VOLATILE)
    TREE_THIS_VOLATILE (*node) = 1;

  return NULL_TREE;
}

static tree
mep_validate_vliw (tree *node, tree name, tree args ATTRIBUTE_UNUSED, 
		   int flags ATTRIBUTE_UNUSED, bool *no_add)
{
  if (TREE_CODE (*node) != FUNCTION_TYPE
      && TREE_CODE (*node) != FUNCTION_DECL
      && TREE_CODE (*node) != METHOD_TYPE
      && TREE_CODE (*node) != FIELD_DECL
      && TREE_CODE (*node) != TYPE_DECL)
    {
      static int gave_pointer_note = 0;
      static int gave_array_note = 0;
      static const char * given_type = NULL;
 
      given_type = tree_code_name[TREE_CODE (*node)];
      if (TREE_CODE (*node) == POINTER_TYPE)
 	given_type = "pointers";
      if (TREE_CODE (*node) == ARRAY_TYPE)
 	given_type = "arrays";
 
      if (given_type)
 	warning (0, "%qE attribute only applies to functions, not %s",
 		 name, given_type);
      else
 	warning (0, "%qE attribute only applies to functions",
 		 name);
      *no_add = true;
 
      if (TREE_CODE (*node) == POINTER_TYPE
 	  && !gave_pointer_note)
 	{
 	  inform (input_location, "to describe a pointer to a VLIW function, use syntax like this:");
 	  inform (input_location, "  typedef int (__vliw *vfuncptr) ();");
 	  gave_pointer_note = 1;
 	}
 
      if (TREE_CODE (*node) == ARRAY_TYPE
 	  && !gave_array_note)
 	{
 	  inform (input_location, "to describe an array of VLIW function pointers, use syntax like this:");
 	  inform (input_location, "  typedef int (__vliw *vfuncptr[]) ();");
 	  gave_array_note = 1;
 	}
    }
  if (!TARGET_VLIW)
    error ("VLIW functions are not allowed without a VLIW configuration");
  return NULL_TREE;
}

static const struct attribute_spec mep_attribute_table[11] =
{
  /* name         min max decl   type   func   handler */
  { "based",        0, 0, false, false, false, mep_validate_based_tiny },
  { "tiny",         0, 0, false, false, false, mep_validate_based_tiny },
  { "near",         0, 0, false, false, false, mep_validate_near_far },
  { "far",          0, 0, false, false, false, mep_validate_near_far },
  { "disinterrupt", 0, 0, false, false, false, mep_validate_disinterrupt },
  { "interrupt",    0, 0, false, false, false, mep_validate_interrupt },
  { "io",           0, 1, false, false, false, mep_validate_io_cb },
  { "cb",           0, 1, false, false, false, mep_validate_io_cb },
  { "vliw",         0, 0, false, true,  false, mep_validate_vliw },
  { NULL,           0, 0, false, false, false, NULL }
};

static bool
mep_function_attribute_inlinable_p (const_tree callee)
{
  tree attrs = TYPE_ATTRIBUTES (TREE_TYPE (callee));
  if (!attrs) attrs = DECL_ATTRIBUTES (callee);
  return (lookup_attribute ("disinterrupt", attrs) == 0
	  && lookup_attribute ("interrupt", attrs) == 0);
}

static bool
mep_can_inline_p (tree caller, tree callee)
{
  if (TREE_CODE (callee) == ADDR_EXPR)
    callee = TREE_OPERAND (callee, 0);
 
  if (!mep_vliw_function_p (caller)
      && mep_vliw_function_p (callee))
    {
      return false;
    }
  return true;
}

#define FUNC_CALL		1
#define FUNC_DISINTERRUPT	2


struct GTY(()) pragma_entry {
  int used;
  int flag;
  const char *funcname;
};
typedef struct pragma_entry pragma_entry;

/* Hash table of farcall-tagged sections.  */
static GTY((param_is (pragma_entry))) htab_t pragma_htab;

static int
pragma_entry_eq (const void *p1, const void *p2)
{
  const pragma_entry *old = (const pragma_entry *) p1;
  const char *new_name = (const char *) p2;

  return strcmp (old->funcname, new_name) == 0;
}

static hashval_t
pragma_entry_hash (const void *p)
{
  const pragma_entry *old = (const pragma_entry *) p;
  return htab_hash_string (old->funcname);
}

static void
mep_note_pragma_flag (const char *funcname, int flag)
{
  pragma_entry **slot;

  if (!pragma_htab)
    pragma_htab = htab_create_ggc (31, pragma_entry_hash,
				    pragma_entry_eq, NULL);

  slot = (pragma_entry **)
    htab_find_slot_with_hash (pragma_htab, funcname,
			      htab_hash_string (funcname), INSERT);

  if (!*slot)
    {
      *slot = ggc_alloc_pragma_entry ();
      (*slot)->flag = 0;
      (*slot)->used = 0;
      (*slot)->funcname = ggc_strdup (funcname);
    }
  (*slot)->flag |= flag;
}

static bool
mep_lookup_pragma_flag (const char *funcname, int flag)
{
  pragma_entry **slot;

  if (!pragma_htab)
    return false;

  if (funcname[0] == '@' && funcname[2] == '.')
    funcname += 3;

  slot = (pragma_entry **)
    htab_find_slot_with_hash (pragma_htab, funcname,
			      htab_hash_string (funcname), NO_INSERT);
  if (slot && *slot && ((*slot)->flag & flag))
    {
      (*slot)->used |= flag;
      return true;
    }
  return false;
}

bool
mep_lookup_pragma_call (const char *funcname)
{
  return mep_lookup_pragma_flag (funcname, FUNC_CALL);
}

void
mep_note_pragma_call (const char *funcname)
{
  mep_note_pragma_flag (funcname, FUNC_CALL);
}

bool
mep_lookup_pragma_disinterrupt (const char *funcname)
{
  return mep_lookup_pragma_flag (funcname, FUNC_DISINTERRUPT);
}

void
mep_note_pragma_disinterrupt (const char *funcname)
{
  mep_note_pragma_flag (funcname, FUNC_DISINTERRUPT);
}

static int
note_unused_pragma_disinterrupt (void **slot, void *data ATTRIBUTE_UNUSED)
{
  const pragma_entry *d = (const pragma_entry *)(*slot);

  if ((d->flag & FUNC_DISINTERRUPT)
      && !(d->used & FUNC_DISINTERRUPT))
    warning (0, "\"#pragma disinterrupt %s\" not used", d->funcname);
  return 1;
}

void
mep_file_cleanups (void)
{
  if (pragma_htab)
    htab_traverse (pragma_htab, note_unused_pragma_disinterrupt, NULL);
}

/* These three functions provide a bridge between the pramgas that
   affect register classes, and the functions that maintain them.  We
   can't call those functions directly as pragma handling is part of
   the front end and doesn't have direct access to them.  */

void
mep_save_register_info (void)
{
  save_register_info ();
}

void
mep_reinit_regs (void)
{
  reinit_regs ();
}

void
mep_init_regs (void)
{
  init_regs ();
}

     

static int
mep_attrlist_to_encoding (tree list, tree decl)
{
  if (mep_multiple_address_regions (list, false) > 1)
    {
      warning (0, "duplicate address region attribute %qE in declaration of %qE on line %d",
	       TREE_PURPOSE (TREE_CHAIN (list)),
	       DECL_NAME (decl),
	       DECL_SOURCE_LINE (decl));
      TREE_CHAIN (list) = NULL_TREE;
    }
      
  while (list)
    {
      if (is_attribute_p ("based", TREE_PURPOSE (list)))
	return 'b';
      if (is_attribute_p ("tiny", TREE_PURPOSE (list)))
	return 't';
      if (is_attribute_p ("near", TREE_PURPOSE (list)))
	return 'n';
      if (is_attribute_p ("far", TREE_PURPOSE (list)))
	return 'f';
      if (is_attribute_p ("io", TREE_PURPOSE (list)))
	{
	  if (TREE_VALUE (list)
	      && TREE_VALUE (TREE_VALUE (list))
	      && TREE_CODE (TREE_VALUE (TREE_VALUE (list))) == INTEGER_CST)
	    {
	      int location = TREE_INT_CST_LOW (TREE_VALUE (TREE_VALUE(list)));
	      if (location >= 0
		  && location <= 0x1000000)
		return 'i';
	    }
	  return 'I';
	}
      if (is_attribute_p ("cb", TREE_PURPOSE (list)))
	return 'c';
      list = TREE_CHAIN (list);
    }
  if (TARGET_TF
      && TREE_CODE (decl) == FUNCTION_DECL
      && DECL_SECTION_NAME (decl) == 0)
    return 'f';
  return 0;
}

static int
mep_comp_type_attributes (const_tree t1, const_tree t2)
{
  int vliw1, vliw2;

  vliw1 = (lookup_attribute ("vliw", TYPE_ATTRIBUTES (t1)) != 0);
  vliw2 = (lookup_attribute ("vliw", TYPE_ATTRIBUTES (t2)) != 0);

  if (vliw1 != vliw2)
    return 0;

  return 1;
}

static void
mep_insert_attributes (tree decl, tree *attributes)
{
  int size;
  const char *secname = 0;
  tree attrib, attrlist;
  char encoding;

  if (TREE_CODE (decl) == FUNCTION_DECL)
    {
      const char *funcname = IDENTIFIER_POINTER (DECL_NAME (decl));

      if (mep_lookup_pragma_disinterrupt (funcname))
	{
	  attrib = build_tree_list (get_identifier ("disinterrupt"), NULL_TREE);
	  *attributes = chainon (*attributes, attrib);
	}
    }

  if (TREE_CODE (decl) != VAR_DECL
      || ! (TREE_PUBLIC (decl) || TREE_STATIC (decl) || DECL_EXTERNAL (decl)))
    return;

  if (TREE_READONLY (decl) && TARGET_DC)
    /* -mdc means that const variables default to the near section,
       regardless of the size cutoff.  */
    return;

  /* User specified an attribute, so override the default.
     Ignore storage attribute of pointed to variable. char __far * x;  */
  if (! (TREE_TYPE (decl) && TREE_CODE (TREE_TYPE (decl)) == POINTER_TYPE))
    {
      if (TYPE_P (decl) && TYPE_ATTRIBUTES (decl) && *attributes)
	TYPE_ATTRIBUTES (decl) = NULL_TREE;
      else if (DECL_ATTRIBUTES (decl) && *attributes)
	DECL_ATTRIBUTES (decl) = NULL_TREE;
    }

  attrlist = *attributes ? *attributes : DECL_ATTRIBUTES (decl);
  encoding = mep_attrlist_to_encoding (attrlist, decl);
  if (!encoding && TYPE_P (TREE_TYPE (decl)))
    {
      attrlist = TYPE_ATTRIBUTES (TREE_TYPE (decl));
      encoding = mep_attrlist_to_encoding (attrlist, decl);
    }
  if (encoding)
    {	  
      /* This means that the declaration has a specific section
	 attribute, so we should not apply the default rules.  */

      if (encoding == 'i' || encoding == 'I')
	{
	  tree attr = lookup_attribute ("io", attrlist);
	  if (attr
	      && TREE_VALUE (attr)
	      && TREE_VALUE (TREE_VALUE(attr)))
	    {
	      int location = TREE_INT_CST_LOW (TREE_VALUE (TREE_VALUE(attr)));
	      static tree previous_value = 0;
	      static int previous_location = 0;
	      static tree previous_name = 0;

	      /* We take advantage of the fact that gcc will reuse the
		 same tree pointer when applying an attribute to a
		 list of decls, but produce a new tree for attributes
		 on separate source lines, even when they're textually
		 identical.  This is the behavior we want.  */
	      if (TREE_VALUE (attr) == previous_value
		  && location == previous_location)
		{
		  warning(0, "__io address 0x%x is the same for %qE and %qE",
			  location, previous_name, DECL_NAME (decl));
		}
	      previous_name = DECL_NAME (decl);
	      previous_location = location;
	      previous_value = TREE_VALUE (attr);
	    }
	}
      return;
    }


  /* Declarations of arrays can change size.  Don't trust them.  */
  if (TREE_CODE (TREE_TYPE (decl)) == ARRAY_TYPE)
    size = 0;
  else
    size = int_size_in_bytes (TREE_TYPE (decl));

  if (TARGET_RAND_TPGP && size <= 4 && size > 0)
    {
      if (TREE_PUBLIC (decl)
	  || DECL_EXTERNAL (decl)
	  || TREE_STATIC (decl))
	{
	  const char *name = IDENTIFIER_POINTER (DECL_NAME (decl));
	  int key = 0;

	  while (*name)
	    key += *name++;

	  switch (key & 3)
	    {
	    case 0:
	      secname = "based";
	      break;
	    case 1:
	      secname = "tiny";
	      break;
	    case 2:
	      secname = "far";
	      break;
	    default:
	      ;
	    }
	}
    }
  else
    {
      if (size <= mep_based_cutoff && size > 0)
	secname = "based";
      else if (size <= mep_tiny_cutoff && size > 0)
	secname = "tiny";
      else if (TARGET_L)
	secname = "far";
    }

  if (mep_const_section && TREE_READONLY (decl))
    {
      if (strcmp (mep_const_section, "tiny") == 0)
	secname = "tiny";
      else if (strcmp (mep_const_section, "near") == 0)
	return;
      else if (strcmp (mep_const_section, "far") == 0)
	secname = "far";
    }

  if (!secname)
    return;

  if (!mep_multiple_address_regions (*attributes, true)
      && !mep_multiple_address_regions (DECL_ATTRIBUTES (decl), false))
    {
      attrib = build_tree_list (get_identifier (secname), NULL_TREE);

      /* Chain the attribute directly onto the variable's DECL_ATTRIBUTES
	 in order to avoid the POINTER_TYPE bypasses in mep_validate_near_far
	 and mep_validate_based_tiny.  */
      DECL_ATTRIBUTES (decl) = chainon (DECL_ATTRIBUTES (decl), attrib);
    }
}

static void
mep_encode_section_info (tree decl, rtx rtl, int first)
{
  rtx rtlname;
  const char *oldname;
  const char *secname;
  char encoding;
  char *newname;
  tree idp;
  int maxsize;
  tree type;
  tree mep_attributes;

  if (! first)
    return;

  if (TREE_CODE (decl) != VAR_DECL
      && TREE_CODE (decl) != FUNCTION_DECL)
    return;

  rtlname = XEXP (rtl, 0);
  if (GET_CODE (rtlname) == SYMBOL_REF)
    oldname = XSTR (rtlname, 0);
  else if (GET_CODE (rtlname) == MEM
	   && GET_CODE (XEXP (rtlname, 0)) == SYMBOL_REF)
    oldname = XSTR (XEXP (rtlname, 0), 0);
  else
    gcc_unreachable ();

  type = TREE_TYPE (decl);
  if (type == error_mark_node)
    return;
  mep_attributes = MEP_ATTRIBUTES (decl);

  encoding = mep_attrlist_to_encoding (mep_attributes, decl);

  if (encoding)
    {
      newname = (char *) alloca (strlen (oldname) + 4);
      sprintf (newname, "@%c.%s", encoding, oldname);
      idp = get_identifier (newname);
      XEXP (rtl, 0) =
	gen_rtx_SYMBOL_REF (Pmode, IDENTIFIER_POINTER (idp));
      SYMBOL_REF_WEAK (XEXP (rtl, 0)) = DECL_WEAK (decl);
      SET_SYMBOL_REF_DECL (XEXP (rtl, 0), decl);

      switch (encoding)
	{
	case 'b':
	  maxsize = 128;
	  secname = "based";
	  break;
	case 't':
	  maxsize = 65536;
	  secname = "tiny";
	  break;
	case 'n':
	  maxsize = 0x1000000;
	  secname = "near";
	  break;
	default:
	  maxsize = 0;
	  secname = 0;
	  break;
	}
      if (maxsize && int_size_in_bytes (TREE_TYPE (decl)) > maxsize)
	{
	  warning (0, "variable %s (%ld bytes) is too large for the %s section (%d bytes)",
		   oldname,
		   (long) int_size_in_bytes (TREE_TYPE (decl)),
		   secname,
		   maxsize);
	}
    }
}

const char *
mep_strip_name_encoding (const char *sym)
{
  while (1)
    {
      if (*sym == '*')
	sym++;
      else if (*sym == '@' && sym[2] == '.')
	sym += 3;
      else
	return sym;
    }
}

static section *
mep_select_section (tree decl, int reloc ATTRIBUTE_UNUSED,
		    unsigned HOST_WIDE_INT align ATTRIBUTE_UNUSED)
{
  int readonly = 1;
  int encoding;

  switch (TREE_CODE (decl))
    {
    case VAR_DECL:
      if (!TREE_READONLY (decl)
	  || TREE_SIDE_EFFECTS (decl)
	  || !DECL_INITIAL (decl)
	  || (DECL_INITIAL (decl) != error_mark_node
	      && !TREE_CONSTANT (DECL_INITIAL (decl))))
	readonly = 0;
      break;
    case CONSTRUCTOR:
      if (! TREE_CONSTANT (decl))
	readonly = 0;
      break;

    default:
      break;
    }

  if (TREE_CODE (decl) == FUNCTION_DECL)
    {
      const char *name = XSTR (XEXP (DECL_RTL (decl), 0), 0);

      if (name[0] == '@' && name[2] == '.')
	encoding = name[1];
      else
	encoding = 0;

      if (flag_function_sections || DECL_ONE_ONLY (decl))
	mep_unique_section (decl, 0);
      else if (lookup_attribute ("vliw", TYPE_ATTRIBUTES (TREE_TYPE (decl))))
	{
	  if (encoding == 'f')
	    return vftext_section;
	  else
	    return vtext_section;
	}
      else if (encoding == 'f')
	return ftext_section;
      else
	return text_section;
    }

  if (TREE_CODE (decl) == VAR_DECL)
    {
      const char *name = XSTR (XEXP (DECL_RTL (decl), 0), 0);

      if (name[0] == '@' && name[2] == '.')
	switch (name[1])
	  {
	  case 'b':
	    return based_section;

	  case 't':
	    if (readonly)
	      return srodata_section;
	    if (DECL_INITIAL (decl))
	      return sdata_section;
	    return tinybss_section;

	  case 'f':
	    if (readonly)
	      return frodata_section;
	    return far_section;

	  case 'i':
	  case 'I':
	    error_at (DECL_SOURCE_LOCATION (decl),
		      "variable %D of type %<io%> must be uninitialized", decl);
	    return data_section;

	  case 'c':
	    error_at (DECL_SOURCE_LOCATION (decl),
		      "variable %D of type %<cb%> must be uninitialized", decl);
	    return data_section;
	  }
    }

  if (readonly)
    return readonly_data_section;

  return data_section;
}

static void
mep_unique_section (tree decl, int reloc)
{
  static const char *prefixes[][2] =
  {
    { ".text.",   ".gnu.linkonce.t." },
    { ".rodata.", ".gnu.linkonce.r." },
    { ".data.",   ".gnu.linkonce.d." },
    { ".based.",   ".gnu.linkonce.based." },
    { ".sdata.",   ".gnu.linkonce.s." },
    { ".far.",     ".gnu.linkonce.far." },
    { ".ftext.",   ".gnu.linkonce.ft." },
    { ".frodata.", ".gnu.linkonce.frd." },
    { ".srodata.", ".gnu.linkonce.srd." },
    { ".vtext.",   ".gnu.linkonce.v." },
    { ".vftext.",   ".gnu.linkonce.vf." }
  };
  int sec = 2; /* .data */
  int len;
  const char *name, *prefix;
  char *string;

  name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl));
  if (DECL_RTL (decl))
    name = XSTR (XEXP (DECL_RTL (decl), 0), 0);

  if (TREE_CODE (decl) == FUNCTION_DECL)
    {
      if (lookup_attribute ("vliw", TYPE_ATTRIBUTES (TREE_TYPE (decl))))
	sec = 9; /* .vtext */
      else
	sec = 0; /* .text */
    }
  else if (decl_readonly_section (decl, reloc))
    sec = 1; /* .rodata */

  if (name[0] == '@' && name[2] == '.')
    {
      switch (name[1])
	{
	case 'b':
	  sec = 3; /* .based */
	  break;
	case 't':
	  if (sec == 1)
	    sec = 8; /* .srodata */
	  else
	    sec = 4; /* .sdata */
	  break;
	case 'f':
	  if (sec == 0)
	    sec = 6; /* .ftext */
	  else if (sec == 9)
	    sec = 10; /* .vftext */
	  else if (sec == 1)
	    sec = 7; /* .frodata */
	  else
	    sec = 5; /* .far. */
	  break;
	}
      name += 3;
    }

  prefix = prefixes[sec][DECL_ONE_ONLY(decl)];
  len    = strlen (name) + strlen (prefix);
  string = (char *) alloca (len + 1);

  sprintf (string, "%s%s", prefix, name);

  DECL_SECTION_NAME (decl) = build_string (len, string);
}

/* Given a decl, a section name, and whether the decl initializer
   has relocs, choose attributes for the section.  */

#define SECTION_MEP_VLIW	SECTION_MACH_DEP

static unsigned int
mep_section_type_flags (tree decl, const char *name, int reloc)
{
  unsigned int flags = default_section_type_flags (decl, name, reloc);

  if (decl && TREE_CODE (decl) == FUNCTION_DECL
      && lookup_attribute ("vliw", TYPE_ATTRIBUTES (TREE_TYPE (decl))))
    flags |= SECTION_MEP_VLIW;

  return flags;
}

/* Switch to an arbitrary section NAME with attributes as specified
   by FLAGS.  ALIGN specifies any known alignment requirements for
   the section; 0 if the default should be used.

   Differs from the standard ELF version only in support of VLIW mode.  */

static void
mep_asm_named_section (const char *name, unsigned int flags, tree decl ATTRIBUTE_UNUSED)
{
  char flagchars[8], *f = flagchars;
  const char *type;

  if (!(flags & SECTION_DEBUG))
    *f++ = 'a';
  if (flags & SECTION_WRITE)
    *f++ = 'w';
  if (flags & SECTION_CODE)
    *f++ = 'x';
  if (flags & SECTION_SMALL)
    *f++ = 's';
  if (flags & SECTION_MEP_VLIW)
    *f++ = 'v';
  *f = '\0';

  if (flags & SECTION_BSS)
    type = "nobits";
  else
    type = "progbits";

  fprintf (asm_out_file, "\t.section\t%s,\"%s\",@%s\n",
	   name, flagchars, type);

  if (flags & SECTION_CODE)
    fputs ((flags & SECTION_MEP_VLIW ? "\t.vliw\n" : "\t.core\n"),
	   asm_out_file);
}

void
mep_output_aligned_common (FILE *stream, tree decl, const char *name,
			   int size, int align, int global)
{
  /* We intentionally don't use mep_section_tag() here.  */
  if (name[0] == '@'
      && (name[1] == 'i' || name[1] == 'I' || name[1] == 'c')
      && name[2] == '.')
    {
      int location = -1;
      tree attr = lookup_attribute ((name[1] == 'c' ? "cb" : "io"),
				    DECL_ATTRIBUTES (decl));
      if (attr
	  && TREE_VALUE (attr)
	  && TREE_VALUE (TREE_VALUE(attr)))
	location = TREE_INT_CST_LOW (TREE_VALUE (TREE_VALUE(attr)));
      if (location == -1)
	return;
      if (global)
	{
	  fprintf (stream, "\t.globl\t");
	  assemble_name (stream, name);
	  fprintf (stream, "\n");
	}
      assemble_name (stream, name);
      fprintf (stream, " = %d\n", location);
      return;
    }
  if (name[0] == '@' && name[2] == '.')
    {
      const char *sec = 0;
      switch (name[1])
	{
	case 'b':
	  switch_to_section (based_section);
	  sec = ".based";
	  break;
	case 't':
	  switch_to_section (tinybss_section);
	  sec = ".sbss";
	  break;
	case 'f':
	  switch_to_section (farbss_section);
	  sec = ".farbss";
	  break;
	}
      if (sec)
	{
	  const char *name2;
	  int p2align = 0;

	  while (align > BITS_PER_UNIT)
	    {
	      align /= 2;
	      p2align ++;
	    }
	  name2 = targetm.strip_name_encoding (name);
	  if (global)
	    fprintf (stream, "\t.globl\t%s\n", name2);
	  fprintf (stream, "\t.p2align %d\n", p2align);
	  fprintf (stream, "\t.type\t%s,@object\n", name2);
	  fprintf (stream, "\t.size\t%s,%d\n", name2, size);
	  fprintf (stream, "%s:\n\t.zero\t%d\n", name2, size);
	  return;
	}
    }

  if (!global)
    {
      fprintf (stream, "\t.local\t");
      assemble_name (stream, name);
      fprintf (stream, "\n");
    }
  fprintf (stream, "\t.comm\t");
  assemble_name (stream, name);
  fprintf (stream, ",%u,%u\n", size, align / BITS_PER_UNIT);
}

/* Trampolines.  */

static void
mep_trampoline_init (rtx m_tramp, tree fndecl, rtx static_chain)
{
  rtx addr = XEXP (m_tramp, 0);
  rtx fnaddr = XEXP (DECL_RTL (fndecl), 0);

  emit_library_call (gen_rtx_SYMBOL_REF (Pmode, "__mep_trampoline_helper"),
		     LCT_NORMAL, VOIDmode, 3,
		     addr, Pmode,
		     fnaddr, Pmode,
		     static_chain, Pmode);
}

/* Experimental Reorg.  */

static bool
mep_mentioned_p (rtx in,
		 rtx reg, /* NULL for mem */
		 int modes_too) /* if nonzero, modes must match also.  */
{
  const char *fmt;
  int i;
  enum rtx_code code;

  if (in == 0)
    return false;
  if (reg && GET_CODE (reg) != REG)
    return false;

  if (GET_CODE (in) == LABEL_REF)
    return (reg == 0);

  code = GET_CODE (in);

  switch (code)
    {
    case MEM:
      if (reg)
	return mep_mentioned_p (XEXP (in, 0), reg, modes_too);
      return true;

    case REG:
      if (!reg)
	return false;
      if (modes_too && (GET_MODE (in) != GET_MODE (reg)))
	return false;
      return (REGNO (in) == REGNO (reg));

    case SCRATCH:
    case CC0:
    case PC:
    case CONST_INT:
    case CONST_DOUBLE:
      return false;

    default:
      break;
    }

  /* Set's source should be read-only.  */
  if (code == SET && !reg)
    return mep_mentioned_p (SET_DEST (in), reg, modes_too);

  fmt = GET_RTX_FORMAT (code);

  for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
    {
      if (fmt[i] == 'E')
	{
	  register int j;
	  for (j = XVECLEN (in, i) - 1; j >= 0; j--)
	    if (mep_mentioned_p (XVECEXP (in, i, j), reg, modes_too))
	      return true;
	}
      else if (fmt[i] == 'e'
	       && mep_mentioned_p (XEXP (in, i), reg, modes_too))
	return true;
    }
  return false;
}

#define EXPERIMENTAL_REGMOVE_REORG 1

#if EXPERIMENTAL_REGMOVE_REORG

static int
mep_compatible_reg_class (int r1, int r2)
{
  if (GR_REGNO_P (r1) && GR_REGNO_P (r2))
    return 1;
  if (CR_REGNO_P (r1) && CR_REGNO_P (r2))
    return 1;
  return 0;
}

static void
mep_reorg_regmove (rtx insns)
{
  rtx insn, next, pat, follow, *where;
  int count = 0, done = 0, replace, before = 0;

  if (dump_file)
    for (insn = insns; insn; insn = NEXT_INSN (insn))
      if (GET_CODE (insn) == INSN)
	before++;

  /* We're looking for (set r2 r1) moves where r1 dies, followed by a
     set that uses the r2 and r2 dies there.  We replace r2 with r1
     and see if it's still a valid insn.  If so, delete the first set.
     Copied from reorg.c.  */

  while (!done)
    {
      done = 1;
      for (insn = insns; insn; insn = next)
	{
	  next = NEXT_INSN (insn);
	  if (GET_CODE (insn) != INSN)
	    continue;
	  pat = PATTERN (insn);

	  replace = 0;

	  if (GET_CODE (pat) == SET
	      && GET_CODE (SET_SRC (pat)) == REG
	      && GET_CODE (SET_DEST (pat)) == REG
	      && find_regno_note (insn, REG_DEAD, REGNO (SET_SRC (pat)))
	      && mep_compatible_reg_class (REGNO (SET_SRC (pat)), REGNO (SET_DEST (pat))))
	    {
	      follow = next_nonnote_insn (insn);
	      if (dump_file)
		fprintf (dump_file, "superfluous moves: considering %d\n", INSN_UID (insn));

	      while (follow && GET_CODE (follow) == INSN
		     && GET_CODE (PATTERN (follow)) == SET
		     && !dead_or_set_p (follow, SET_SRC (pat))
		     && !mep_mentioned_p (PATTERN (follow), SET_SRC (pat), 0)
		     && !mep_mentioned_p (PATTERN (follow), SET_DEST (pat), 0))
		{
		  if (dump_file)
		    fprintf (dump_file, "\tskipping %d\n", INSN_UID (follow));
		  follow = next_nonnote_insn (follow);
		}

	      if (dump_file)
		fprintf (dump_file, "\tfollow is %d\n", INSN_UID (follow));
	      if (follow && GET_CODE (follow) == INSN
		  && GET_CODE (PATTERN (follow)) == SET
		  && find_regno_note (follow, REG_DEAD, REGNO (SET_DEST (pat))))
		{
		  if (GET_CODE (SET_DEST (PATTERN (follow))) == REG)
		    {
		      if (mep_mentioned_p (SET_SRC (PATTERN (follow)), SET_DEST (pat), 1))
			{
			  replace = 1;
			  where = & SET_SRC (PATTERN (follow));
			}
		    }
		  else if (GET_CODE (SET_DEST (PATTERN (follow))) == MEM)
		    {
		      if (mep_mentioned_p (PATTERN (follow), SET_DEST (pat), 1))
			{
			  replace = 1;
			  where = & PATTERN (follow);
			}
		    }
		}
	    }

	  /* If so, follow is the corresponding insn */
	  if (replace)
	    {
	      if (dump_file)
		{
		  rtx x;

		  fprintf (dump_file, "----- Candidate for superfluous move deletion:\n\n");
		  for (x = insn; x ;x = NEXT_INSN (x))
		    {
		      print_rtl_single (dump_file, x);
		      if (x == follow)
			break;
		      fprintf (dump_file, "\n");
		    }
		}

	      if (validate_replace_rtx_subexp (SET_DEST (pat), SET_SRC (pat),
					       follow, where))
		{
		  count ++;
		  next = delete_insn (insn);
		  if (dump_file)
		    {
		      fprintf (dump_file, "\n----- Success!  new insn:\n\n");
		      print_rtl_single (dump_file, follow);
		    }
		  done = 0;
		}
	    }
	}
    }

  if (dump_file)
    {
      fprintf (dump_file, "\n%d insn%s deleted out of %d.\n\n", count, count == 1 ? "" : "s", before);
      fprintf (dump_file, "=====\n");
    }
}
#endif


/* Figure out where to put LABEL, which is the label for a repeat loop.
   If INCLUDING, LAST_INSN is the last instruction in the loop, otherwise
   the loop ends just before LAST_INSN.  If SHARED, insns other than the
   "repeat" might use LABEL to jump to the loop's continuation point.

   Return the last instruction in the adjusted loop.  */

static rtx
mep_insert_repeat_label_last (rtx last_insn, rtx label, bool including,
			      bool shared)
{
  rtx next, prev;
  int count = 0, code, icode;

  if (dump_file)
    fprintf (dump_file, "considering end of repeat loop at insn %d\n",
	     INSN_UID (last_insn));

  /* Set PREV to the last insn in the loop.  */
  prev = last_insn;
  if (!including)
    prev = PREV_INSN (prev);

  /* Set NEXT to the next insn after the repeat label.  */
  next = last_insn;
  if (!shared)
    while (prev != 0)
      {
	code = GET_CODE (prev);
	if (code == CALL_INSN || code == CODE_LABEL || code == BARRIER)
	  break;

	if (INSN_P (prev))
	  {
	    if (GET_CODE (PATTERN (prev)) == SEQUENCE)
	      prev = XVECEXP (PATTERN (prev), 0, 1);

	    /* Other insns that should not be in the last two opcodes.  */
	    icode = recog_memoized (prev);
	    if (icode < 0
		|| icode == CODE_FOR_repeat
		|| icode == CODE_FOR_erepeat
		|| get_attr_may_trap (prev) == MAY_TRAP_YES)
	      break;

	    /* That leaves JUMP_INSN and INSN.  It will have BImode if it
	       is the second instruction in a VLIW bundle.  In that case,
	       loop again: if the first instruction also satisfies the
	       conditions above then we will reach here again and put
	       both of them into the repeat epilogue.  Otherwise both
	       should remain outside.  */
	    if (GET_MODE (prev) != BImode)
	      {
		count++;
		next = prev;
		if (dump_file)
		  print_rtl_single (dump_file, next);
		if (count == 2)
		  break;
	      }
	  }
	prev = PREV_INSN (prev);
      }

  /* See if we're adding the label immediately after the repeat insn.
     If so, we need to separate them with a nop.  */
  prev = prev_real_insn (next);
  if (prev)
    switch (recog_memoized (prev))
      {
      case CODE_FOR_repeat:
      case CODE_FOR_erepeat:
	if (dump_file)
	  fprintf (dump_file, "Adding nop inside loop\n");
	emit_insn_before (gen_nop (), next);
	break;

      default:
	break;
      }

  /* Insert the label.  */
  emit_label_before (label, next);

  /* Insert the nops.  */
  if (dump_file && count < 2)
    fprintf (dump_file, "Adding %d nop%s\n\n",
	     2 - count, count == 1 ? "" : "s");

  for (; count < 2; count++)
    if (including)
      last_insn = emit_insn_after (gen_nop (), last_insn);
    else
      emit_insn_before (gen_nop (), last_insn);

  return last_insn;
}


void
mep_emit_doloop (rtx *operands, int is_end)
{
  rtx tag;

  if (cfun->machine->doloop_tags == 0
      || cfun->machine->doloop_tag_from_end == is_end)
    {
      cfun->machine->doloop_tags++;
      cfun->machine->doloop_tag_from_end = is_end;
    }

  tag = GEN_INT (cfun->machine->doloop_tags - 1);
  if (is_end)
    emit_jump_insn (gen_doloop_end_internal (operands[0], operands[4], tag));
  else
    emit_insn (gen_doloop_begin_internal (operands[0], operands[0], tag));
}


/* Code for converting doloop_begins and doloop_ends into valid
   MeP instructions.  A doloop_begin is just a placeholder:

	$count = unspec ($count)

   where $count is initially the number of iterations - 1.
   doloop_end has the form:

	if ($count-- == 0) goto label

   The counter variable is private to the doloop insns, nothing else
   relies on its value.

   There are three cases, in decreasing order of preference:

      1. A loop has exactly one doloop_begin and one doloop_end.
	 The doloop_end branches to the first instruction after
	 the doloop_begin.

	 In this case we can replace the doloop_begin with a repeat
	 instruction and remove the doloop_end.  I.e.:

		$count1 = unspec ($count1)
	    label:
		...
		insn1
		insn2
		if ($count2-- == 0) goto label

	  becomes:

		repeat $count1,repeat_label
	    label:
		...
	    repeat_label:
		insn1
		insn2
		# end repeat

      2. As for (1), except there are several doloop_ends.  One of them
	 (call it X) falls through to a label L.  All the others fall
	 through to branches to L.

	 In this case, we remove X and replace the other doloop_ends
	 with branches to the repeat label.  For example:

		$count1 = unspec ($count1)
	    start:
		...
		if ($count2-- == 0) goto label
	    end:
		...
		if ($count3-- == 0) goto label
		goto end

	 becomes:

		repeat $count1,repeat_label
	    start:
		...
	    repeat_label:
		nop
		nop
		# end repeat
	    end:
		...
		goto repeat_label

      3. The fallback case.  Replace doloop_begins with:

		$count = $count + 1

	 Replace doloop_ends with the equivalent of:

		$count = $count - 1
		if ($count == 0) goto label

	 Note that this might need a scratch register if $count
	 is stored in memory.  */

/* A structure describing one doloop_begin.  */
struct mep_doloop_begin {
  /* The next doloop_begin with the same tag.  */
  struct mep_doloop_begin *next;

  /* The instruction itself.  */
  rtx insn;

  /* The initial counter value.  This is known to be a general register.  */
  rtx counter;
};

/* A structure describing a doloop_end.  */
struct mep_doloop_end {
  /* The next doloop_end with the same loop tag.  */
  struct mep_doloop_end *next;

  /* The instruction itself.  */
  rtx insn;

  /* The first instruction after INSN when the branch isn't taken.  */
  rtx fallthrough;

  /* The location of the counter value.  Since doloop_end_internal is a
     jump instruction, it has to allow the counter to be stored anywhere
     (any non-fixed register or memory location).  */
  rtx counter;

  /* The target label (the place where the insn branches when the counter
     isn't zero).  */
  rtx label;

  /* A scratch register.  Only available when COUNTER isn't stored
     in a general register.  */
  rtx scratch;
};


/* One do-while loop.  */
struct mep_doloop {
  /* All the doloop_begins for this loop (in no particular order).  */
  struct mep_doloop_begin *begin;

  /* All the doloop_ends.  When there is more than one, arrange things
     so that the first one is the most likely to be X in case (2) above.  */
  struct mep_doloop_end *end;
};


/* Return true if LOOP can be converted into repeat/repeat_end form
   (that is, if it matches cases (1) or (2) above).  */

static bool
mep_repeat_loop_p (struct mep_doloop *loop)
{
  struct mep_doloop_end *end;
  rtx fallthrough;

  /* There must be exactly one doloop_begin and at least one doloop_end.  */
  if (loop->begin == 0 || loop->end == 0 || loop->begin->next != 0)
    return false;

  /* The first doloop_end (X) must branch back to the insn after
     the doloop_begin.  */
  if (prev_real_insn (loop->end->label) != loop->begin->insn)
    return false;

  /* All the other doloop_ends must branch to the same place as X.
     When the branch isn't taken, they must jump to the instruction
     after X.  */
  fallthrough = loop->end->fallthrough;
  for (end = loop->end->next; end != 0; end = end->next)
    if (end->label != loop->end->label
	|| !simplejump_p (end->fallthrough)
	|| next_real_insn (JUMP_LABEL (end->fallthrough)) != fallthrough)
      return false;

  return true;
}


/* The main repeat reorg function.  See comment above for details.  */

static void
mep_reorg_repeat (rtx insns)
{
  rtx insn;
  struct mep_doloop *loops, *loop;
  struct mep_doloop_begin *begin;
  struct mep_doloop_end *end;

  /* Quick exit if we haven't created any loops.  */
  if (cfun->machine->doloop_tags == 0)
    return;

  /* Create an array of mep_doloop structures.  */
  loops = (struct mep_doloop *) alloca (sizeof (loops[0]) * cfun->machine->doloop_tags);
  memset (loops, 0, sizeof (loops[0]) * cfun->machine->doloop_tags);

  /* Search the function for do-while insns and group them by loop tag.  */
  for (insn = insns; insn; insn = NEXT_INSN (insn))
    if (INSN_P (insn))
      switch (recog_memoized (insn))
	{
	case CODE_FOR_doloop_begin_internal:
	  insn_extract (insn);
	  loop = &loops[INTVAL (recog_data.operand[2])];

	  begin = (struct mep_doloop_begin *) alloca (sizeof (struct mep_doloop_begin));
	  begin->next = loop->begin;
	  begin->insn = insn;
	  begin->counter = recog_data.operand[0];

	  loop->begin = begin;
	  break;

	case CODE_FOR_doloop_end_internal:
	  insn_extract (insn);
	  loop = &loops[INTVAL (recog_data.operand[2])];

	  end = (struct mep_doloop_end *) alloca (sizeof (struct mep_doloop_end));
	  end->insn = insn;
	  end->fallthrough = next_real_insn (insn);
	  end->counter = recog_data.operand[0];
	  end->label = recog_data.operand[1];
	  end->scratch = recog_data.operand[3];

	  /* If this insn falls through to an unconditional jump,
	     give it a lower priority than the others.  */
	  if (loop->end != 0 && simplejump_p (end->fallthrough))
	    {
	      end->next = loop->end->next;
	      loop->end->next = end;
	    }
	  else
	    {
	      end->next = loop->end;
	      loop->end = end;
	    }
	  break;
	}

  /* Convert the insns for each loop in turn.  */
  for (loop = loops; loop < loops + cfun->machine->doloop_tags; loop++)
    if (mep_repeat_loop_p (loop))
      {
	/* Case (1) or (2).  */
	rtx repeat_label, label_ref;

	/* Create a new label for the repeat insn.  */
	repeat_label = gen_label_rtx ();

	/* Replace the doloop_begin with a repeat.  */
	label_ref = gen_rtx_LABEL_REF (VOIDmode, repeat_label);
	emit_insn_before (gen_repeat (loop->begin->counter, label_ref),
			  loop->begin->insn);
	delete_insn (loop->begin->insn);

	/* Insert the repeat label before the first doloop_end.
	   Fill the gap with nops if there are other doloop_ends.  */
	mep_insert_repeat_label_last (loop->end->insn, repeat_label,
				      false, loop->end->next != 0);

	/* Emit a repeat_end (to improve the readability of the output).  */
	emit_insn_before (gen_repeat_end (), loop->end->insn);

	/* Delete the first doloop_end.  */
	delete_insn (loop->end->insn);

	/* Replace the others with branches to REPEAT_LABEL.  */
	for (end = loop->end->next; end != 0; end = end->next)
	  {
	    emit_jump_insn_before (gen_jump (repeat_label), end->insn);
	    delete_insn (end->insn);
	    delete_insn (end->fallthrough);
	  }
      }
    else
      {
	/* Case (3).  First replace all the doloop_begins with increment
	   instructions.  */
	for (begin = loop->begin; begin != 0; begin = begin->next)
	  {
	    emit_insn_before (gen_add3_insn (copy_rtx (begin->counter),
					     begin->counter, const1_rtx),
			      begin->insn);
	    delete_insn (begin->insn);
	  }

	/* Replace all the doloop_ends with decrement-and-branch sequences.  */
	for (end = loop->end; end != 0; end = end->next)
	  {
	    rtx reg;

	    start_sequence ();

	    /* Load the counter value into a general register.  */
	    reg = end->counter;
	    if (!REG_P (reg) || REGNO (reg) > 15)
	      {
		reg = end->scratch;
		emit_move_insn (copy_rtx (reg), copy_rtx (end->counter));
	      }

	    /* Decrement the counter.  */
	    emit_insn (gen_add3_insn (copy_rtx (reg), copy_rtx (reg),
				      constm1_rtx));

	    /* Copy it back to its original location.  */
	    if (reg != end->counter)
	      emit_move_insn (copy_rtx (end->counter), copy_rtx (reg));

	    /* Jump back to the start label.  */
	    insn = emit_jump_insn (gen_mep_bne_true (reg, const0_rtx,
						     end->label));
	    JUMP_LABEL (insn) = end->label;
	    LABEL_NUSES (end->label)++;

	    /* Emit the whole sequence before the doloop_end.  */
	    insn = get_insns ();
	    end_sequence ();
	    emit_insn_before (insn, end->insn);

	    /* Delete the doloop_end.  */
	    delete_insn (end->insn);
	  }
      }
}


static bool
mep_invertable_branch_p (rtx insn)
{
  rtx cond, set;
  enum rtx_code old_code;
  int i;

  set = PATTERN (insn);
  if (GET_CODE (set) != SET)
    return false;
  if (GET_CODE (XEXP (set, 1)) != IF_THEN_ELSE)
    return false;
  cond = XEXP (XEXP (set, 1), 0);
  old_code = GET_CODE (cond);
  switch (old_code)
    {
    case EQ:
      PUT_CODE (cond, NE);
      break;
    case NE:
      PUT_CODE (cond, EQ);
      break;
    case LT:
      PUT_CODE (cond, GE);
      break;
    case GE:
      PUT_CODE (cond, LT);
      break;
    default:
      return false;
    }
  INSN_CODE (insn) = -1;
  i = recog_memoized (insn);
  PUT_CODE (cond, old_code);
  INSN_CODE (insn) = -1;
  return i >= 0;
}

static void
mep_invert_branch (rtx insn, rtx after)
{
  rtx cond, set, label;
  int i;

  set = PATTERN (insn);

  gcc_assert (GET_CODE (set) == SET);
  gcc_assert (GET_CODE (XEXP (set, 1)) == IF_THEN_ELSE);

  cond = XEXP (XEXP (set, 1), 0);
  switch (GET_CODE (cond))
    {
    case EQ:
      PUT_CODE (cond, NE);
      break;
    case NE:
      PUT_CODE (cond, EQ);
      break;
    case LT:
      PUT_CODE (cond, GE);
      break;
    case GE:
      PUT_CODE (cond, LT);
      break;
    default:
      gcc_unreachable ();
    }
  label = gen_label_rtx ();
  emit_label_after (label, after);
  for (i=1; i<=2; i++)
    if (GET_CODE (XEXP (XEXP (set, 1), i)) == LABEL_REF)
      {
	rtx ref = XEXP (XEXP (set, 1), i);
	if (LABEL_NUSES (XEXP (ref, 0)) == 1)
	  delete_insn (XEXP (ref, 0));
	XEXP (ref, 0) = label;
	LABEL_NUSES (label) ++;
	JUMP_LABEL (insn) = label;
      }
  INSN_CODE (insn) = -1;
  i = recog_memoized (insn);
  gcc_assert (i >= 0);
}

static void
mep_reorg_erepeat (rtx insns)
{
  rtx insn, prev, l, x;
  int count;

  for (insn = insns; insn; insn = NEXT_INSN (insn))
    if (JUMP_P (insn)
	&& ! JUMP_TABLE_DATA_P (insn)
	&& mep_invertable_branch_p (insn))
      {
	if (dump_file)
	  {
	    fprintf (dump_file, "\n------------------------------\n");
	    fprintf (dump_file, "erepeat: considering this jump:\n");
	    print_rtl_single (dump_file, insn);
	  }
	count = simplejump_p (insn) ? 0 : 1;
	for (prev = PREV_INSN (insn); prev; prev = PREV_INSN (prev))
	  {
	    if (GET_CODE (prev) == CALL_INSN
		|| BARRIER_P (prev))
	      break;

	    if (prev == JUMP_LABEL (insn))
	      {
		rtx newlast;
		if (dump_file)
		  fprintf (dump_file, "found loop top, %d insns\n", count);

		if (LABEL_NUSES (prev) == 1)
		  /* We're the only user, always safe */ ;
		else if (LABEL_NUSES (prev) == 2)
		  {
		    /* See if there's a barrier before this label.  If
		       so, we know nobody inside the loop uses it.
		       But we must be careful to put the erepeat
		       *after* the label.  */
		    rtx barrier;
		    for (barrier = PREV_INSN (prev);
			 barrier && GET_CODE (barrier) == NOTE;
			 barrier = PREV_INSN (barrier))
		      ;
		    if (barrier && GET_CODE (barrier) != BARRIER)
		      break;
		  }
		else
		  {
		    /* We don't know who else, within or without our loop, uses this */
		    if (dump_file)
		      fprintf (dump_file, "... but there are multiple users, too risky.\n");
		    break;
		  }

		/* Generate a label to be used by the erepat insn.  */
		l = gen_label_rtx ();

		/* Insert the erepeat after INSN's target label.  */
		x = gen_erepeat (gen_rtx_LABEL_REF (VOIDmode, l));
		LABEL_NUSES (l)++;
		emit_insn_after (x, prev);

		/* Insert the erepeat label.  */
		newlast = (mep_insert_repeat_label_last
			   (insn, l, !simplejump_p (insn), false));
		if (simplejump_p (insn))
		  {
		    emit_insn_before (gen_erepeat_end (), insn);
		    delete_insn (insn);
		  }
		else
		  {
		    mep_invert_branch (insn, newlast);
		    emit_insn_after (gen_erepeat_end (), newlast);
		  }
		break;
	      }

	    if (LABEL_P (prev))
	      {
		/* A label is OK if there is exactly one user, and we
		   can find that user before the next label.  */
		rtx user = 0;
		int safe = 0;
		if (LABEL_NUSES (prev) == 1)
		  {
		    for (user = PREV_INSN (prev);
			 user && (INSN_P (user) || GET_CODE (user) == NOTE);
			 user = PREV_INSN (user))
		      if (GET_CODE (user) == JUMP_INSN
			  && JUMP_LABEL (user) == prev)
			{
			  safe = INSN_UID (user);
			  break;
			}
		  }
		if (!safe)
		  break;
		if (dump_file)
		  fprintf (dump_file, "... ignoring jump from insn %d to %d\n",
			   safe, INSN_UID (prev));
	      }

	    if (INSN_P (prev))
	      {
		count ++;
	      }
	  }
      }
  if (dump_file)
    fprintf (dump_file, "\n==============================\n");
}
  
/* Replace a jump to a return, with a copy of the return.  GCC doesn't
   always do this on its own.  */

static void
mep_jmp_return_reorg (rtx insns)
{
  rtx insn, label, ret;
  int ret_code;

  for (insn = insns; insn; insn = NEXT_INSN (insn))
    if (simplejump_p (insn))
    {
      /* Find the fist real insn the jump jumps to.  */
      label = ret = JUMP_LABEL (insn);
      while (ret
	     && (GET_CODE (ret) == NOTE
		 || GET_CODE (ret) == CODE_LABEL
		 || GET_CODE (PATTERN (ret)) == USE))
	ret = NEXT_INSN (ret);

      if (ret)
	{
	  /* Is it a return?  */
	  ret_code = recog_memoized (ret);
	  if (ret_code == CODE_FOR_return_internal
	      || ret_code == CODE_FOR_eh_return_internal)
	    {
	      /* It is.  Replace the jump with a return.  */
	      LABEL_NUSES (label) --;
	      if (LABEL_NUSES (label) == 0)
		delete_insn (label);
	      PATTERN (insn) = copy_rtx (PATTERN (ret));
	      INSN_CODE (insn) = -1;
	    }
	}
    }
}


static void
mep_reorg_addcombine (rtx insns)
{
  rtx i, n;

  for (i = insns; i; i = NEXT_INSN (i))
    if (INSN_P (i)
	&& INSN_CODE (i) == CODE_FOR_addsi3
	&& GET_CODE (SET_DEST (PATTERN (i))) == REG
	&& GET_CODE (XEXP (SET_SRC (PATTERN (i)), 0)) == REG
	&& REGNO (SET_DEST (PATTERN (i))) == REGNO (XEXP (SET_SRC (PATTERN (i)), 0))
	&& GET_CODE (XEXP (SET_SRC (PATTERN (i)), 1)) == CONST_INT)
      {
	n = NEXT_INSN (i);
	if (INSN_P (n)
	    && INSN_CODE (n) == CODE_FOR_addsi3
	    && GET_CODE (SET_DEST (PATTERN (n))) == REG
	    && GET_CODE (XEXP (SET_SRC (PATTERN (n)), 0)) == REG
	    && REGNO (SET_DEST (PATTERN (n))) == REGNO (XEXP (SET_SRC (PATTERN (n)), 0))
	    && GET_CODE (XEXP (SET_SRC (PATTERN (n)), 1)) == CONST_INT)
	  {
	    int ic = INTVAL (XEXP (SET_SRC (PATTERN (i)), 1));
	    int nc = INTVAL (XEXP (SET_SRC (PATTERN (n)), 1));
	    if (REGNO (SET_DEST (PATTERN (i))) == REGNO (SET_DEST (PATTERN (n)))
		&& ic + nc < 32767
		&& ic + nc > -32768)
	      {
		XEXP (SET_SRC (PATTERN (i)), 1) = GEN_INT (ic + nc);
		NEXT_INSN (i) = NEXT_INSN (n);
		if (NEXT_INSN (i))
		  PREV_INSN (NEXT_INSN (i)) = i;
	      }
	  }
      }
}

/* If this insn adjusts the stack, return the adjustment, else return
   zero.  */
static int
add_sp_insn_p (rtx insn)
{
  rtx pat;

  if (! single_set (insn))
    return 0;
  pat = PATTERN (insn);
  if (GET_CODE (SET_DEST (pat)) != REG)
    return 0;
  if (REGNO (SET_DEST (pat)) != SP_REGNO)
    return 0;
  if (GET_CODE (SET_SRC (pat)) != PLUS)
    return 0;
  if (GET_CODE (XEXP (SET_SRC (pat), 0)) != REG)
    return 0;
  if (REGNO (XEXP (SET_SRC (pat), 0)) != SP_REGNO)
    return 0;
  if (GET_CODE (XEXP (SET_SRC (pat), 1)) != CONST_INT)
    return 0;
  return INTVAL (XEXP (SET_SRC (pat), 1));
}

/* Check for trivial functions that set up an unneeded stack
   frame.  */
static void
mep_reorg_noframe (rtx insns)
{
  rtx start_frame_insn;
  rtx end_frame_insn = 0;
  int sp_adjust, sp2;
  rtx sp;

  /* The first insn should be $sp = $sp + N */
  while (insns && ! INSN_P (insns))
    insns = NEXT_INSN (insns);
  if (!insns)
    return;

  sp_adjust = add_sp_insn_p (insns);
  if (sp_adjust == 0)
    return;

  start_frame_insn = insns;
  sp = SET_DEST (PATTERN (start_frame_insn));

  insns = next_real_insn (insns);

  while (insns)
    {
      rtx next = next_real_insn (insns);
      if (!next)
	break;

      sp2 = add_sp_insn_p (insns);
      if (sp2)
	{
	  if (end_frame_insn)
	    return;
	  end_frame_insn = insns;
	  if (sp2 != -sp_adjust)
	    return;
	}
      else if (mep_mentioned_p (insns, sp, 0))
	return;
      else if (CALL_P (insns))
	return;

      insns = next;
    }

  if (end_frame_insn)
    {
      delete_insn (start_frame_insn);
      delete_insn (end_frame_insn);
    }
}

static void
mep_reorg (void)
{
  rtx insns = get_insns ();

  /* We require accurate REG_DEAD notes.  */
  compute_bb_for_insn ();
  df_note_add_problem ();
  df_analyze ();

  mep_reorg_addcombine (insns);
#if EXPERIMENTAL_REGMOVE_REORG
  /* VLIW packing has been done already, so we can't just delete things.  */
  if (!mep_vliw_function_p (cfun->decl))
    mep_reorg_regmove (insns);
#endif
  mep_jmp_return_reorg (insns);
  mep_bundle_insns (insns);
  mep_reorg_repeat (insns);
  if (optimize
      && !profile_flag
      && !profile_arc_flag
      && TARGET_OPT_REPEAT
      && (!mep_interrupt_p () || mep_interrupt_saved_reg (RPB_REGNO)))
    mep_reorg_erepeat (insns);

  /* This may delete *insns so make sure it's last.  */
  mep_reorg_noframe (insns);

  df_finish_pass (false);
}



/*----------------------------------------------------------------------*/
/* Builtins								*/
/*----------------------------------------------------------------------*/

/* Element X gives the index into cgen_insns[] of the most general
   implementation of intrinsic X.  Unimplemented intrinsics are
   mapped to -1.  */
int mep_intrinsic_insn[ARRAY_SIZE (cgen_intrinsics)];

/* Element X gives the index of another instruction that is mapped to
   the same intrinsic as cgen_insns[X].  It is -1 when there is no other
   instruction.

   Things are set up so that mep_intrinsic_chain[X] < X.  */
static int mep_intrinsic_chain[ARRAY_SIZE (cgen_insns)];

/* The bitmask for the current ISA.  The ISA masks are declared
   in mep-intrin.h.  */
unsigned int mep_selected_isa;

struct mep_config {
  const char *config_name;
  unsigned int isa;
};

static struct mep_config mep_configs[] = {
#ifdef COPROC_SELECTION_TABLE
  COPROC_SELECTION_TABLE,
#endif
  { 0, 0 }
};

/* Initialize the global intrinsics variables above.  */

static void
mep_init_intrinsics (void)
{
  size_t i;

  /* Set MEP_SELECTED_ISA to the ISA flag for this configuration.  */
  mep_selected_isa = mep_configs[0].isa;
  if (mep_config_string != 0)
    for (i = 0; mep_configs[i].config_name; i++)
      if (strcmp (mep_config_string, mep_configs[i].config_name) == 0)
	{
	  mep_selected_isa = mep_configs[i].isa;
	  break;
	}

  /* Assume all intrinsics are unavailable.  */
  for (i = 0; i < ARRAY_SIZE (mep_intrinsic_insn); i++)
    mep_intrinsic_insn[i] = -1;

  /* Build up the global intrinsic tables.  */
  for (i = 0; i < ARRAY_SIZE (cgen_insns); i++)
    if ((cgen_insns[i].isas & mep_selected_isa) != 0)
      {
	mep_intrinsic_chain[i] = mep_intrinsic_insn[cgen_insns[i].intrinsic];
	mep_intrinsic_insn[cgen_insns[i].intrinsic] = i;
      }
  /* See whether we can directly move values between one coprocessor
     register and another.  */
  for (i = 0; i < ARRAY_SIZE (mep_cmov_insns); i++)
    if (MEP_INTRINSIC_AVAILABLE_P (mep_cmov_insns[i]))
      mep_have_copro_copro_moves_p = true;

  /* See whether we can directly move values between core and
     coprocessor registers.  */
  mep_have_core_copro_moves_p = (MEP_INTRINSIC_AVAILABLE_P (mep_cmov1)
                                 && MEP_INTRINSIC_AVAILABLE_P (mep_cmov2));

  mep_have_core_copro_moves_p = 1;
}

/* Declare all available intrinsic functions.  Called once only.  */

static tree cp_data_bus_int_type_node;
static tree opaque_vector_type_node;
static tree v8qi_type_node;
static tree v4hi_type_node;
static tree v2si_type_node;
static tree v8uqi_type_node;
static tree v4uhi_type_node;
static tree v2usi_type_node;

static tree
mep_cgen_regnum_to_type (enum cgen_regnum_operand_type cr)
{
  switch (cr)
    {
    case cgen_regnum_operand_type_POINTER:	return ptr_type_node;
    case cgen_regnum_operand_type_LONG:		return long_integer_type_node;
    case cgen_regnum_operand_type_ULONG:	return long_unsigned_type_node;
    case cgen_regnum_operand_type_SHORT:	return short_integer_type_node;
    case cgen_regnum_operand_type_USHORT:	return short_unsigned_type_node;
    case cgen_regnum_operand_type_CHAR:		return char_type_node;
    case cgen_regnum_operand_type_UCHAR:	return unsigned_char_type_node;
    case cgen_regnum_operand_type_SI:		return intSI_type_node;
    case cgen_regnum_operand_type_DI:		return intDI_type_node;
    case cgen_regnum_operand_type_VECTOR:	return opaque_vector_type_node;
    case cgen_regnum_operand_type_V8QI:		return v8qi_type_node;
    case cgen_regnum_operand_type_V4HI:		return v4hi_type_node;
    case cgen_regnum_operand_type_V2SI:		return v2si_type_node;
    case cgen_regnum_operand_type_V8UQI:	return v8uqi_type_node;
    case cgen_regnum_operand_type_V4UHI:	return v4uhi_type_node;
    case cgen_regnum_operand_type_V2USI:	return v2usi_type_node;
    case cgen_regnum_operand_type_CP_DATA_BUS_INT: return cp_data_bus_int_type_node;
    default:
      return void_type_node;
    }
}

static void
mep_init_builtins (void)
{
  size_t i;

  if (TARGET_64BIT_CR_REGS)
    cp_data_bus_int_type_node = long_long_integer_type_node;
  else
    cp_data_bus_int_type_node = long_integer_type_node;

  opaque_vector_type_node = build_opaque_vector_type (intQI_type_node, 8);
  v8qi_type_node = build_vector_type (intQI_type_node, 8);
  v4hi_type_node = build_vector_type (intHI_type_node, 4);
  v2si_type_node = build_vector_type (intSI_type_node, 2);
  v8uqi_type_node = build_vector_type (unsigned_intQI_type_node, 8);
  v4uhi_type_node = build_vector_type (unsigned_intHI_type_node, 4);
  v2usi_type_node = build_vector_type (unsigned_intSI_type_node, 2);

  (*lang_hooks.decls.pushdecl)
    (build_decl (BUILTINS_LOCATION, TYPE_DECL, get_identifier ("cp_data_bus_int"),
		 cp_data_bus_int_type_node));

  (*lang_hooks.decls.pushdecl)
    (build_decl (BUILTINS_LOCATION, TYPE_DECL, get_identifier ("cp_vector"),
		 opaque_vector_type_node));

  (*lang_hooks.decls.pushdecl)
    (build_decl (BUILTINS_LOCATION, TYPE_DECL, get_identifier ("cp_v8qi"),
		 v8qi_type_node));
  (*lang_hooks.decls.pushdecl)
    (build_decl (BUILTINS_LOCATION, TYPE_DECL, get_identifier ("cp_v4hi"),
		 v4hi_type_node));
  (*lang_hooks.decls.pushdecl)
    (build_decl (BUILTINS_LOCATION, TYPE_DECL, get_identifier ("cp_v2si"),
		 v2si_type_node));

  (*lang_hooks.decls.pushdecl)
    (build_decl (BUILTINS_LOCATION, TYPE_DECL, get_identifier ("cp_v8uqi"),
		 v8uqi_type_node));
  (*lang_hooks.decls.pushdecl)
    (build_decl (BUILTINS_LOCATION, TYPE_DECL, get_identifier ("cp_v4uhi"),
		 v4uhi_type_node));
  (*lang_hooks.decls.pushdecl)
    (build_decl (BUILTINS_LOCATION, TYPE_DECL, get_identifier ("cp_v2usi"),
		 v2usi_type_node));

  /* Intrinsics like mep_cadd3 are implemented with two groups of
     instructions, one which uses UNSPECs and one which uses a specific
     rtl code such as PLUS.  Instructions in the latter group belong
     to GROUP_KNOWN_CODE.

     In such cases, the intrinsic will have two entries in the global
     tables above.  The unspec form is accessed using builtin functions
     while the specific form is accessed using the mep_* enum in
     mep-intrin.h.

     The idea is that __cop arithmetic and builtin functions have
     different optimization requirements.  If mep_cadd3() appears in
     the source code, the user will surely except gcc to use cadd3
     rather than a work-alike such as add3.  However, if the user
     just writes "a + b", where a or b are __cop variables, it is
     reasonable for gcc to choose a core instruction rather than
     cadd3 if it believes that is more optimal.  */
  for (i = 0; i < ARRAY_SIZE (cgen_insns); i++)
    if ((cgen_insns[i].groups & GROUP_KNOWN_CODE) == 0
	&& mep_intrinsic_insn[cgen_insns[i].intrinsic] >= 0)
      {
	tree ret_type = void_type_node;
	tree bi_type;

	if (i > 0 && cgen_insns[i].intrinsic == cgen_insns[i-1].intrinsic)
	  continue;

	if (cgen_insns[i].cret_p)
	  ret_type = mep_cgen_regnum_to_type (cgen_insns[i].regnums[0].type);

	bi_type = build_function_type (ret_type, 0);
	add_builtin_function (cgen_intrinsics[cgen_insns[i].intrinsic],
			      bi_type,
			      cgen_insns[i].intrinsic, BUILT_IN_MD, NULL, NULL);
      }
}

/* Report the unavailablity of the given intrinsic.  */

#if 1
static void
mep_intrinsic_unavailable (int intrinsic)
{
  static int already_reported_p[ARRAY_SIZE (cgen_intrinsics)];

  if (already_reported_p[intrinsic])
    return;

  if (mep_intrinsic_insn[intrinsic] < 0)
    error ("coprocessor intrinsic %qs is not available in this configuration",
	   cgen_intrinsics[intrinsic]);
  else if (CGEN_CURRENT_GROUP == GROUP_VLIW)
    error ("%qs is not available in VLIW functions",
	   cgen_intrinsics[intrinsic]);
  else
    error ("%qs is not available in non-VLIW functions",
	   cgen_intrinsics[intrinsic]);

  already_reported_p[intrinsic] = 1;
}
#endif


/* See if any implementation of INTRINSIC is available to the
   current function.  If so, store the most general implementation
   in *INSN_PTR and return true.  Return false otherwise.  */

static bool
mep_get_intrinsic_insn (int intrinsic ATTRIBUTE_UNUSED, const struct cgen_insn **insn_ptr ATTRIBUTE_UNUSED)
{
  int i;

  i = mep_intrinsic_insn[intrinsic];
  while (i >= 0 && !CGEN_ENABLE_INSN_P (i))
    i = mep_intrinsic_chain[i];

  if (i >= 0)
    {
      *insn_ptr = &cgen_insns[i];
      return true;
    }
  return false;
}


/* Like mep_get_intrinsic_insn, but with extra handling for moves.
   If INTRINSIC is mep_cmov, but there is no pure CR <- CR move insn,
   try using a work-alike instead.  In this case, the returned insn
   may have three operands rather than two.  */

static bool
mep_get_move_insn (int intrinsic, const struct cgen_insn **cgen_insn)
{
  size_t i;

  if (intrinsic == mep_cmov)
    {
      for (i = 0; i < ARRAY_SIZE (mep_cmov_insns); i++)
	if (mep_get_intrinsic_insn (mep_cmov_insns[i], cgen_insn))
	  return true;
      return false;
    }
  return mep_get_intrinsic_insn (intrinsic, cgen_insn);
}


/* If ARG is a register operand that is the same size as MODE, convert it
   to MODE using a subreg.  Otherwise return ARG as-is.  */

static rtx
mep_convert_arg (enum machine_mode mode, rtx arg)
{
  if (GET_MODE (arg) != mode
      && register_operand (arg, VOIDmode)
      && GET_MODE_SIZE (GET_MODE (arg)) == GET_MODE_SIZE (mode))
    return simplify_gen_subreg (mode, arg, GET_MODE (arg), 0);
  return arg;
}


/* Apply regnum conversions to ARG using the description given by REGNUM.
   Return the new argument on success and null on failure.  */

static rtx
mep_convert_regnum (const struct cgen_regnum_operand *regnum, rtx arg)
{
  if (regnum->count == 0)
    return arg;

  if (GET_CODE (arg) != CONST_INT
      || INTVAL (arg) < 0
      || INTVAL (arg) >= regnum->count)
    return 0;

  return gen_rtx_REG (SImode, INTVAL (arg) + regnum->base);
}


/* Try to make intrinsic argument ARG match the given operand.
   UNSIGNED_P is true if the argument has an unsigned type.  */

static rtx
mep_legitimize_arg (const struct insn_operand_data *operand, rtx arg,
		    int unsigned_p)
{
  if (GET_CODE (arg) == CONST_INT)
    {
      /* CONST_INTs can only be bound to integer operands.  */
      if (GET_MODE_CLASS (operand->mode) != MODE_INT)
	return 0;
    }
  else if (GET_CODE (arg) == CONST_DOUBLE)
    /* These hold vector constants.  */;
  else if (GET_MODE_SIZE (GET_MODE (arg)) != GET_MODE_SIZE (operand->mode))
    {
      /* If the argument is a different size from what's expected, we must
	 have a value in the right mode class in order to convert it.  */
      if (GET_MODE_CLASS (operand->mode) != GET_MODE_CLASS (GET_MODE (arg)))
	return 0;

      /* If the operand is an rvalue, promote or demote it to match the
	 operand's size.  This might not need extra instructions when
	 ARG is a register value.  */
      if (operand->constraint[0] != '=')
	arg = convert_to_mode (operand->mode, arg, unsigned_p);
    }

  /* If the operand is an lvalue, bind the operand to a new register.
     The caller will copy this value into ARG after the main
     instruction.  By doing this always, we produce slightly more
     optimal code.  */
  /* But not for control registers.  */
  if (operand->constraint[0] == '='
      && (! REG_P (arg)
	  || ! (CONTROL_REGNO_P (REGNO (arg))
		|| CCR_REGNO_P (REGNO (arg))
		|| CR_REGNO_P (REGNO (arg)))
	  ))
    return gen_reg_rtx (operand->mode);

  /* Try simple mode punning.  */
  arg = mep_convert_arg (operand->mode, arg);
  if (operand->predicate (arg, operand->mode))
    return arg;

  /* See if forcing the argument into a register will make it match.  */
  if (GET_CODE (arg) == CONST_INT || GET_CODE (arg) == CONST_DOUBLE)
    arg = force_reg (operand->mode, arg);
  else
    arg = mep_convert_arg (operand->mode, force_reg (GET_MODE (arg), arg));
  if (operand->predicate (arg, operand->mode))
    return arg;

  return 0;
}


/* Report that ARG cannot be passed to argument ARGNUM of intrinsic
   function FNNAME.  OPERAND describes the operand to which ARGNUM
   is mapped.  */

static void
mep_incompatible_arg (const struct insn_operand_data *operand, rtx arg,
		      int argnum, tree fnname)
{
  size_t i;

  if (GET_CODE (arg) == CONST_INT)
    for (i = 0; i < ARRAY_SIZE (cgen_immediate_predicates); i++)
      if (operand->predicate == cgen_immediate_predicates[i].predicate)
	{
	  const struct cgen_immediate_predicate *predicate;
	  HOST_WIDE_INT argval;

	  predicate = &cgen_immediate_predicates[i];
	  argval = INTVAL (arg);
	  if (argval < predicate->lower || argval >= predicate->upper)
	    error ("argument %d of %qE must be in the range %d...%d",
		   argnum, fnname, predicate->lower, predicate->upper - 1);
	  else
	    error ("argument %d of %qE must be a multiple of %d",
		   argnum, fnname, predicate->align);
	  return;
	}

  error ("incompatible type for argument %d of %qE", argnum, fnname);
}

static rtx
mep_expand_builtin (tree exp, rtx target ATTRIBUTE_UNUSED,
		    rtx subtarget ATTRIBUTE_UNUSED,
		    enum machine_mode mode ATTRIBUTE_UNUSED,
		    int ignore ATTRIBUTE_UNUSED)
{
  rtx pat, op[10], arg[10];
  unsigned int a;
  int opindex, unsigned_p[10];
  tree fndecl, args;
  unsigned int n_args;
  tree fnname;
  const struct cgen_insn *cgen_insn;
  const struct insn_data_d *idata;
  unsigned int first_arg = 0;
  unsigned int builtin_n_args;

  fndecl = TREE_OPERAND (CALL_EXPR_FN (exp), 0);
  fnname = DECL_NAME (fndecl);

  /* Find out which instruction we should emit.  Note that some coprocessor
     intrinsics may only be available in VLIW mode, or only in normal mode.  */
  if (!mep_get_intrinsic_insn (DECL_FUNCTION_CODE (fndecl), &cgen_insn))
    {
      mep_intrinsic_unavailable (DECL_FUNCTION_CODE (fndecl));
      return NULL_RTX;
    }
  idata = &insn_data[cgen_insn->icode];

  builtin_n_args = cgen_insn->num_args;

  if (cgen_insn->cret_p)
    {
      if (cgen_insn->cret_p > 1)
	builtin_n_args ++;
      first_arg = 1;
      mep_cgen_regnum_to_type (cgen_insn->regnums[0].type);
      builtin_n_args --;
    }

  /* Evaluate each argument.  */
  n_args = call_expr_nargs (exp);

  if (n_args < builtin_n_args)
    {
      error ("too few arguments to %qE", fnname);
      return NULL_RTX;
    }
  if (n_args > builtin_n_args)
    {
      error ("too many arguments to %qE", fnname);
      return NULL_RTX;
    }

  for (a = first_arg; a < builtin_n_args + first_arg; a++)
    {
      tree value;

      args = CALL_EXPR_ARG (exp, a - first_arg);

      value = args;

#if 0
      if (cgen_insn->regnums[a].reference_p)
	{
	  if (TREE_CODE (value) != ADDR_EXPR)
	    {
	      debug_tree(value);
	      error ("argument %d of %qE must be an address", a+1, fnname);
	      return NULL_RTX;
	    }
	  value = TREE_OPERAND (value, 0);
	}
#endif

      /* If the argument has been promoted to int, get the unpromoted
	 value.  This is necessary when sub-int memory values are bound
	 to reference parameters.  */
      if (TREE_CODE (value) == NOP_EXPR
	  && TREE_TYPE (value) == integer_type_node
	  && INTEGRAL_TYPE_P (TREE_TYPE (TREE_OPERAND (value, 0)))
	  && (TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (value, 0)))
	      < TYPE_PRECISION (TREE_TYPE (value))))
	value = TREE_OPERAND (value, 0);

      /* If the argument has been promoted to double, get the unpromoted
	 SFmode value.  This is necessary for FMAX support, for example.  */
      if (TREE_CODE (value) == NOP_EXPR
	  && SCALAR_FLOAT_TYPE_P (TREE_TYPE (value))
	  && SCALAR_FLOAT_TYPE_P (TREE_TYPE (TREE_OPERAND (value, 0)))
	  && TYPE_MODE (TREE_TYPE (value)) == DFmode
	  && TYPE_MODE (TREE_TYPE (TREE_OPERAND (value, 0))) == SFmode)
	value = TREE_OPERAND (value, 0);

      unsigned_p[a] = TYPE_UNSIGNED (TREE_TYPE (value));
      arg[a] = expand_expr (value, NULL, VOIDmode, EXPAND_NORMAL);
      arg[a] = mep_convert_regnum (&cgen_insn->regnums[a], arg[a]);
      if (cgen_insn->regnums[a].reference_p)
	{
	  tree pointed_to = TREE_TYPE (TREE_TYPE (value));
	  enum machine_mode pointed_mode = TYPE_MODE (pointed_to);

	  arg[a] = gen_rtx_MEM (pointed_mode, arg[a]);
	}
      if (arg[a] == 0)
	{
	  error ("argument %d of %qE must be in the range %d...%d",
		 a + 1, fnname, 0, cgen_insn->regnums[a].count - 1);
	  return NULL_RTX;
	}
    }

  for (a = 0; a < first_arg; a++)
    {
      if (a == 0 && target && GET_MODE (target) == idata->operand[0].mode)
	arg[a] = target;
      else
	arg[a] = gen_reg_rtx (idata->operand[0].mode);
    }

  /* Convert the arguments into a form suitable for the intrinsic.
     Report an error if this isn't possible.  */
  for (opindex = 0; opindex < idata->n_operands; opindex++)
    {
      a = cgen_insn->op_mapping[opindex];
      op[opindex] = mep_legitimize_arg (&idata->operand[opindex],
					arg[a], unsigned_p[a]);
      if (op[opindex] == 0)
	{
	  mep_incompatible_arg (&idata->operand[opindex],
				arg[a], a + 1 - first_arg, fnname);
	  return NULL_RTX;
	}
    }

  /* Emit the instruction.  */
  pat = idata->genfun (op[0], op[1], op[2], op[3], op[4],
		       op[5], op[6], op[7], op[8], op[9]);

  if (GET_CODE (pat) == SET
      && GET_CODE (SET_DEST (pat)) == PC
      && GET_CODE (SET_SRC (pat)) == IF_THEN_ELSE)
    emit_jump_insn (pat);
  else
    emit_insn (pat);

  /* Copy lvalues back to their final locations.  */
  for (opindex = 0; opindex < idata->n_operands; opindex++)
    if (idata->operand[opindex].constraint[0] == '=')
      {
	a = cgen_insn->op_mapping[opindex];
	if (a >= first_arg)
	  {
	    if (GET_MODE_CLASS (GET_MODE (arg[a]))
		!= GET_MODE_CLASS (GET_MODE (op[opindex])))
	      emit_move_insn (arg[a], gen_lowpart (GET_MODE (arg[a]),
						   op[opindex]));
	    else
	      {
		/* First convert the operand to the right mode, then copy it
		   into the destination.  Doing the conversion as a separate
		   step (rather than using convert_move) means that we can
		   avoid creating no-op moves when ARG[A] and OP[OPINDEX]
		   refer to the same register.  */
		op[opindex] = convert_to_mode (GET_MODE (arg[a]),
					       op[opindex], unsigned_p[a]);
		if (!rtx_equal_p (arg[a], op[opindex]))
		  emit_move_insn (arg[a], op[opindex]);
	      }
	  }
      }

  if (first_arg > 0 && target && target != op[0])
    {
      emit_move_insn (target, op[0]);
    }

  return target;
}

static bool
mep_vector_mode_supported_p (enum machine_mode mode ATTRIBUTE_UNUSED)
{
  return false;
}

/* A subroutine of global_reg_mentioned_p, returns 1 if *LOC mentions
   a global register.  */

static int
global_reg_mentioned_p_1 (rtx *loc, void *data ATTRIBUTE_UNUSED)
{
  int regno;
  rtx x = *loc;

  if (! x)
    return 0;

  switch (GET_CODE (x))
    {
    case SUBREG:
      if (REG_P (SUBREG_REG (x)))
	{
	  if (REGNO (SUBREG_REG (x)) < FIRST_PSEUDO_REGISTER
	      && global_regs[subreg_regno (x)])
	    return 1;
	  return 0;
	}
      break;

    case REG:
      regno = REGNO (x);
      if (regno < FIRST_PSEUDO_REGISTER && global_regs[regno])
	return 1;
      return 0;

    case SCRATCH:
    case PC:
    case CC0:
    case CONST_INT:
    case CONST_DOUBLE:
    case CONST:
    case LABEL_REF:
      return 0;

    case CALL:
      /* A non-constant call might use a global register.  */
      return 1;

    default:
      break;
    }

  return 0;
}

/* Returns nonzero if X mentions a global register.  */

static int
global_reg_mentioned_p (rtx x)
{
  if (INSN_P (x))
    {
      if (CALL_P (x))
	{
	  if (! RTL_CONST_OR_PURE_CALL_P (x))
	    return 1;
	  x = CALL_INSN_FUNCTION_USAGE (x);
	  if (x == 0)
	    return 0;
	}
      else
	x = PATTERN (x);
    }

  return for_each_rtx (&x, global_reg_mentioned_p_1, NULL);
}
/* Scheduling hooks for VLIW mode.

   Conceptually this is very simple: we have a two-pack architecture
   that takes one core insn and one coprocessor insn to make up either
   a 32- or 64-bit instruction word (depending on the option bit set in
   the chip).  I.e. in VL32 mode, we can pack one 16-bit core insn and
   one 16-bit cop insn; in VL64 mode we can pack one 16-bit core insn
   and one 48-bit cop insn or two 32-bit core/cop insns.

   In practice, instruction selection will be a bear.  Consider in
   VL64 mode the following insns

	add $1, 1
	cmov $cr0, $0

   these cannot pack, since the add is a 16-bit core insn and cmov
   is a 32-bit cop insn.  However,

	add3 $1, $1, 1
	cmov $cr0, $0

   packs just fine.  For good VLIW code generation in VL64 mode, we
   will have to have 32-bit alternatives for many of the common core
   insns.  Not implemented.  */

static int
mep_adjust_cost (rtx insn, rtx link, rtx dep_insn, int cost)
{
  int cost_specified;

  if (REG_NOTE_KIND (link) != 0)
    {
      /* See whether INSN and DEP_INSN are intrinsics that set the same
	 hard register.  If so, it is more important to free up DEP_INSN
	 than it is to free up INSN.

	 Note that intrinsics like mep_mulr are handled differently from
	 the equivalent mep.md patterns.  In mep.md, if we don't care
	 about the value of $lo and $hi, the pattern will just clobber
	 the registers, not set them.  Since clobbers don't count as
	 output dependencies, it is often possible to reorder two mulrs,
	 even after reload.

	 In contrast, mep_mulr() sets both $lo and $hi to specific values,
	 so any pair of mep_mulr()s will be inter-dependent.   We should
	 therefore give the first mep_mulr() a higher priority.  */
      if (REG_NOTE_KIND (link) == REG_DEP_OUTPUT
	  && global_reg_mentioned_p (PATTERN (insn))
	  && global_reg_mentioned_p (PATTERN (dep_insn)))
	return 1;

      /* If the dependence is an anti or output dependence, assume it
	 has no cost.  */
      return 0;
    }

  /* If we can't recognize the insns, we can't really do anything.  */
  if (recog_memoized (dep_insn) < 0)
    return cost;

  /* The latency attribute doesn't apply to MeP-h1: we use the stall
     attribute instead.  */
  if (!TARGET_H1)
    {
      cost_specified = get_attr_latency (dep_insn);
      if (cost_specified != 0)
	return cost_specified;
    }

  return cost;
}

/* ??? We don't properly compute the length of a load/store insn,
   taking into account the addressing mode.  */

static int
mep_issue_rate (void)
{
  return TARGET_IVC2 ? 3 : 2;
}

/* Return true if function DECL was declared with the vliw attribute.  */

bool
mep_vliw_function_p (tree decl)
{
  return lookup_attribute ("vliw", TYPE_ATTRIBUTES (TREE_TYPE (decl))) != 0;
}

static rtx
mep_find_ready_insn (rtx *ready, int nready, enum attr_slot slot, int length)
{
  int i;

  for (i = nready - 1; i >= 0; --i)
    {
      rtx insn = ready[i];
      if (recog_memoized (insn) >= 0
	  && get_attr_slot (insn) == slot
	  && get_attr_length (insn) == length)
	return insn;
    }

  return NULL_RTX;
}

static void
mep_move_ready_insn (rtx *ready, int nready, rtx insn)
{
  int i;

  for (i = 0; i < nready; ++i)
    if (ready[i] == insn)
      {
	for (; i < nready - 1; ++i)
	  ready[i] = ready[i + 1];
	ready[i] = insn;
	return;
      }

  gcc_unreachable ();
}

static void
mep_print_sched_insn (FILE *dump, rtx insn)
{
  const char *slots = "none";
  const char *name = NULL;
  int code;
  char buf[30];

  if (GET_CODE (PATTERN (insn)) == SET
      || GET_CODE (PATTERN (insn)) == PARALLEL)
    {
      switch (get_attr_slots (insn))
	{
	case SLOTS_CORE: slots = "core"; break;
	case SLOTS_C3: slots = "c3"; break;
	case SLOTS_P0: slots = "p0"; break;
	case SLOTS_P0_P0S: slots = "p0,p0s"; break;
	case SLOTS_P0_P1: slots = "p0,p1"; break;
	case SLOTS_P0S: slots = "p0s"; break;
	case SLOTS_P0S_P1: slots = "p0s,p1"; break;
	case SLOTS_P1: slots = "p1"; break;
	default:
	  sprintf(buf, "%d", get_attr_slots (insn));
	  slots = buf;
	  break;
	}
    }
  if (GET_CODE (PATTERN (insn)) == USE)
    slots = "use";

  code = INSN_CODE (insn);
  if (code >= 0)
    name = get_insn_name (code);
  if (!name)
    name = "{unknown}";

  fprintf (dump,
	   "insn %4d %4d  %8s  %s\n",
	   code,
	   INSN_UID (insn),
	   name,
	   slots);
}

static int
mep_sched_reorder (FILE *dump ATTRIBUTE_UNUSED,
		   int sched_verbose ATTRIBUTE_UNUSED, rtx *ready,
		   int *pnready, int clock ATTRIBUTE_UNUSED)
{
  int nready = *pnready;
  rtx core_insn, cop_insn;
  int i;

  if (dump && sched_verbose > 1)
    {
      fprintf (dump, "\nsched_reorder: clock %d nready %d\n", clock, nready);
      for (i=0; i<nready; i++)
	mep_print_sched_insn (dump, ready[i]);
      fprintf (dump, "\n");
    }

  if (!mep_vliw_function_p (cfun->decl))
    return 1;
  if (nready < 2)
    return 1;

  /* IVC2 uses a DFA to determine what's ready and what's not. */
  if (TARGET_IVC2)
    return nready;

  /* We can issue either a core or coprocessor instruction.
     Look for a matched pair of insns to reorder.  If we don't
     find any, don't second-guess the scheduler's priorities.  */

  if ((core_insn = mep_find_ready_insn (ready, nready, SLOT_CORE, 2))
      && (cop_insn = mep_find_ready_insn (ready, nready, SLOT_COP,
					  TARGET_OPT_VL64 ? 6 : 2)))
    ;
  else if (TARGET_OPT_VL64
	   && (core_insn = mep_find_ready_insn (ready, nready, SLOT_CORE, 4))
	   && (cop_insn = mep_find_ready_insn (ready, nready, SLOT_COP, 4)))
    ;
  else
    /* We didn't find a pair.  Issue the single insn at the head
       of the ready list.  */
    return 1;

  /* Reorder the two insns first.  */
  mep_move_ready_insn (ready, nready, core_insn);
  mep_move_ready_insn (ready, nready - 1, cop_insn);
  return 2;
}

/* A for_each_rtx callback.  Return true if *X is a register that is
   set by insn PREV.  */

static int
mep_store_find_set (rtx *x, void *prev)
{
  return REG_P (*x) && reg_set_p (*x, (const_rtx) prev);
}

/* Like mep_store_bypass_p, but takes a pattern as the second argument,
   not the containing insn.  */

static bool
mep_store_data_bypass_1 (rtx prev, rtx pat)
{
  /* Cope with intrinsics like swcpa.  */
  if (GET_CODE (pat) == PARALLEL)
    {
      int i;

      for (i = 0; i < XVECLEN (pat, 0); i++)
	if (mep_store_data_bypass_p (prev, XVECEXP (pat, 0, i)))
	  return true;

      return false;
    }

  /* Check for some sort of store.  */
  if (GET_CODE (pat) != SET
      || GET_CODE (SET_DEST (pat)) != MEM)
    return false;

  /* Intrinsics use patterns of the form (set (mem (scratch)) (unspec ...)).
     The first operand to the unspec is the store data and the other operands
     are used to calculate the address.  */
  if (GET_CODE (SET_SRC (pat)) == UNSPEC)
    {
      rtx src;
      int i;

      src = SET_SRC (pat);
      for (i = 1; i < XVECLEN (src, 0); i++)
	if (for_each_rtx (&XVECEXP (src, 0, i), mep_store_find_set, prev))
	  return false;

      return true;
    }

  /* Otherwise just check that PREV doesn't modify any register mentioned
     in the memory destination.  */
  return !for_each_rtx (&SET_DEST (pat), mep_store_find_set, prev);
}

/* Return true if INSN is a store instruction and if the store address
   has no true dependence on PREV.  */

bool
mep_store_data_bypass_p (rtx prev, rtx insn)
{
  return INSN_P (insn) ? mep_store_data_bypass_1 (prev, PATTERN (insn)) : false;
}

/* A for_each_rtx subroutine of mep_mul_hilo_bypass_p.  Return 1 if *X
   is a register other than LO or HI and if PREV sets *X.  */

static int
mep_mul_hilo_bypass_1 (rtx *x, void *prev)
{
  return (REG_P (*x)
	  && REGNO (*x) != LO_REGNO
	  && REGNO (*x) != HI_REGNO
	  && reg_set_p (*x, (const_rtx) prev));
}

/* Return true if, apart from HI/LO, there are no true dependencies
   between multiplication instructions PREV and INSN.  */

bool
mep_mul_hilo_bypass_p (rtx prev, rtx insn)
{
  rtx pat;

  pat = PATTERN (insn);
  if (GET_CODE (pat) == PARALLEL)
    pat = XVECEXP (pat, 0, 0);
  return (GET_CODE (pat) == SET
	  && !for_each_rtx (&SET_SRC (pat), mep_mul_hilo_bypass_1, prev));
}

/* Return true if INSN is an ldc instruction that issues to the
   MeP-h1 integer pipeline.  This is true for instructions that
   read from PSW, LP, SAR, HI and LO.  */

bool
mep_ipipe_ldc_p (rtx insn)
{
  rtx pat, src;

  pat = PATTERN (insn);

  /* Cope with instrinsics that set both a hard register and its shadow.
     The set of the hard register comes first.  */
  if (GET_CODE (pat) == PARALLEL)
    pat = XVECEXP (pat, 0, 0);

  if (GET_CODE (pat) == SET)
    {
      src = SET_SRC (pat);

      /* Cope with intrinsics.  The first operand to the unspec is
	 the source register.  */
      if (GET_CODE (src) == UNSPEC || GET_CODE (src) == UNSPEC_VOLATILE)
	src = XVECEXP (src, 0, 0);

      if (REG_P (src))
	switch (REGNO (src))
	  {
	  case PSW_REGNO:
	  case LP_REGNO:
	  case SAR_REGNO:
	  case HI_REGNO:
	  case LO_REGNO:
	    return true;
	  }
    }
  return false;
}

/* Create a VLIW bundle from core instruction CORE and coprocessor
   instruction COP.  COP always satisfies INSN_P, but CORE can be
   either a new pattern or an existing instruction.

   Emit the bundle in place of COP and return it.  */

static rtx
mep_make_bundle (rtx core, rtx cop)
{
  rtx insn;

  /* If CORE is an existing instruction, remove it, otherwise put
     the new pattern in an INSN harness.  */
  if (INSN_P (core))
    remove_insn (core);
  else
    core = make_insn_raw (core);

  /* Generate the bundle sequence and replace COP with it.  */
  insn = gen_rtx_SEQUENCE (VOIDmode, gen_rtvec (2, core, cop));
  insn = emit_insn_after (insn, cop);
  remove_insn (cop);

  /* Set up the links of the insns inside the SEQUENCE.  */
  PREV_INSN (core) = PREV_INSN (insn);
  NEXT_INSN (core) = cop;
  PREV_INSN (cop) = core;
  NEXT_INSN (cop) = NEXT_INSN (insn);

  /* Set the VLIW flag for the coprocessor instruction.  */
  PUT_MODE (core, VOIDmode);
  PUT_MODE (cop, BImode);

  /* Derive a location for the bundle.  Individual instructions cannot
     have their own location because there can be no assembler labels
     between CORE and COP.  */
  INSN_LOCATOR (insn) = INSN_LOCATOR (INSN_LOCATOR (core) ? core : cop);
  INSN_LOCATOR (core) = 0;
  INSN_LOCATOR (cop) = 0;

  return insn;
}

/* A helper routine for ms1_insn_dependent_p called through note_stores.  */

static void
mep_insn_dependent_p_1 (rtx x, const_rtx pat ATTRIBUTE_UNUSED, void *data)
{
  rtx * pinsn = (rtx *) data;

  if (*pinsn && reg_mentioned_p (x, *pinsn))
    *pinsn = NULL_RTX;
}

/* Return true if anything in insn X is (anti,output,true) dependent on
   anything in insn Y.  */

static int
mep_insn_dependent_p (rtx x, rtx y)
{
  rtx tmp;

  gcc_assert (INSN_P (x));
  gcc_assert (INSN_P (y));

  tmp = PATTERN (y);
  note_stores (PATTERN (x), mep_insn_dependent_p_1, &tmp);
  if (tmp == NULL_RTX)
    return 1;

  tmp = PATTERN (x);
  note_stores (PATTERN (y), mep_insn_dependent_p_1, &tmp);
  if (tmp == NULL_RTX)
    return 1;

  return 0;
}

static int
core_insn_p (rtx insn)
{
  if (GET_CODE (PATTERN (insn)) == USE)
    return 0;
  if (get_attr_slot (insn) == SLOT_CORE)
    return 1;
  return 0;
}

/* Mark coprocessor instructions that can be bundled together with
   the immediately preceeding core instruction.  This is later used
   to emit the "+" that tells the assembler to create a VLIW insn.

   For unbundled insns, the assembler will automatically add coprocessor
   nops, and 16-bit core nops.  Due to an apparent oversight in the
   spec, the assembler will _not_ automatically add 32-bit core nops,
   so we have to emit those here.

   Called from mep_insn_reorg.  */

static void
mep_bundle_insns (rtx insns)
{
  rtx insn, last = NULL_RTX, first = NULL_RTX;
  int saw_scheduling = 0;

  /* Only do bundling if we're in vliw mode.  */
  if (!mep_vliw_function_p (cfun->decl))
    return;

  /* The first insn in a bundle are TImode, the remainder are
     VOIDmode.  After this function, the first has VOIDmode and the
     rest have BImode.  */

  /* Note: this doesn't appear to be true for JUMP_INSNs.  */

  /* First, move any NOTEs that are within a bundle, to the beginning
     of the bundle.  */
  for (insn = insns; insn ; insn = NEXT_INSN (insn))
    {
      if (NOTE_P (insn) && first)
	/* Don't clear FIRST.  */;

      else if (NONJUMP_INSN_P (insn) && GET_MODE (insn) == TImode)
	first = insn;

      else if (NONJUMP_INSN_P (insn) && GET_MODE (insn) == VOIDmode && first)
	{
	  rtx note, prev;

	  /* INSN is part of a bundle; FIRST is the first insn in that
	     bundle.  Move all intervening notes out of the bundle.
	     In addition, since the debug pass may insert a label
	     whenever the current line changes, set the location info
	     for INSN to match FIRST.  */

	  INSN_LOCATOR (insn) = INSN_LOCATOR (first);

	  note = PREV_INSN (insn);
	  while (note && note != first)
	    {
	      prev = PREV_INSN (note);

	      if (NOTE_P (note))
		{
		  /* Remove NOTE from here... */
		  PREV_INSN (NEXT_INSN (note)) = PREV_INSN (note);
		  NEXT_INSN (PREV_INSN (note)) = NEXT_INSN (note);
		  /* ...and put it in here.  */
		  NEXT_INSN (note) = first;
		  PREV_INSN (note) = PREV_INSN (first);
		  NEXT_INSN (PREV_INSN (note)) = note;
		  PREV_INSN (NEXT_INSN (note)) = note;
		}

	      note = prev;
	    }
	}

      else if (!NONJUMP_INSN_P (insn))
	first = 0;
    }

  /* Now fix up the bundles.  */
  for (insn = insns; insn ; insn = NEXT_INSN (insn))
    {
      if (NOTE_P (insn))
	continue;

      if (!NONJUMP_INSN_P (insn))
	{
	  last = 0;
	  continue;
	}

      /* If we're not optimizing enough, there won't be scheduling
	 info.  We detect that here.  */
      if (GET_MODE (insn) == TImode)
	saw_scheduling = 1;
      if (!saw_scheduling)
	continue;

      if (TARGET_IVC2)
	{
	  rtx core_insn = NULL_RTX;

	  /* IVC2 slots are scheduled by DFA, so we just accept
	     whatever the scheduler gives us.  However, we must make
	     sure the core insn (if any) is the first in the bundle.
	     The IVC2 assembler can insert whatever NOPs are needed,
	     and allows a COP insn to be first.  */

	  if (NONJUMP_INSN_P (insn)
	      && GET_CODE (PATTERN (insn)) != USE
	      && GET_MODE (insn) == TImode)
	    {
	      for (last = insn;
		   NEXT_INSN (last)
		     && GET_MODE (NEXT_INSN (last)) == VOIDmode
		     && NONJUMP_INSN_P (NEXT_INSN (last));
		   last = NEXT_INSN (last))
		{
		  if (core_insn_p (last))
		    core_insn = last;
		}
	      if (core_insn_p (last))
		core_insn = last;

	      if (core_insn && core_insn != insn)
		{
		  /* Swap core insn to first in the bundle.  */

		  /* Remove core insn.  */
		  if (PREV_INSN (core_insn))
		    NEXT_INSN (PREV_INSN (core_insn)) = NEXT_INSN (core_insn);
		  if (NEXT_INSN (core_insn))
		    PREV_INSN (NEXT_INSN (core_insn)) = PREV_INSN (core_insn);

		  /* Re-insert core insn.  */
		  PREV_INSN (core_insn) = PREV_INSN (insn);
		  NEXT_INSN (core_insn) = insn;

		  if (PREV_INSN (core_insn))
		    NEXT_INSN (PREV_INSN (core_insn)) = core_insn;
		  PREV_INSN (insn) = core_insn;

		  PUT_MODE (core_insn, TImode);
		  PUT_MODE (insn, VOIDmode);
		}
	    }

	  /* The first insn has TImode, the rest have VOIDmode */
	  if (GET_MODE (insn) == TImode)
	    PUT_MODE (insn, VOIDmode);
	  else
	    PUT_MODE (insn, BImode);
	  continue;
	}

      PUT_MODE (insn, VOIDmode);
      if (recog_memoized (insn) >= 0
	  && get_attr_slot (insn) == SLOT_COP)
	{
	  if (GET_CODE (insn) == JUMP_INSN
	      || ! last
	      || recog_memoized (last) < 0
	      || get_attr_slot (last) != SLOT_CORE
	      || (get_attr_length (insn)
		  != (TARGET_OPT_VL64 ? 8 : 4) - get_attr_length (last))
	      || mep_insn_dependent_p (insn, last))
	    {
	      switch (get_attr_length (insn))
		{
		case 8:
		  break;
		case 6:
		  insn = mep_make_bundle (gen_nop (), insn);
		  break;
		case 4:
		  if (TARGET_OPT_VL64)
		    insn = mep_make_bundle (gen_nop32 (), insn);
		  break;
		case 2:
		  if (TARGET_OPT_VL64)
		    error ("2 byte cop instructions are"
			   " not allowed in 64-bit VLIW mode");
		  else
		    insn = mep_make_bundle (gen_nop (), insn);
		  break;
		default:
		  error ("unexpected %d byte cop instruction",
			 get_attr_length (insn));
		  break;
		}
	    }
	  else
	    insn = mep_make_bundle (last, insn);
	}

      last = insn;
    }
}


/* Try to instantiate INTRINSIC with the operands given in OPERANDS.
   Return true on success.  This function can fail if the intrinsic
   is unavailable or if the operands don't satisfy their predicates.  */

bool
mep_emit_intrinsic (int intrinsic, const rtx *operands)
{
  const struct cgen_insn *cgen_insn;
  const struct insn_data_d *idata;
  rtx newop[10];
  int i;

  if (!mep_get_intrinsic_insn (intrinsic, &cgen_insn))
    return false;

  idata = &insn_data[cgen_insn->icode];
  for (i = 0; i < idata->n_operands; i++)
    {
      newop[i] = mep_convert_arg (idata->operand[i].mode, operands[i]);
      if (!idata->operand[i].predicate (newop[i], idata->operand[i].mode))
	return false;
    }

  emit_insn (idata->genfun (newop[0], newop[1], newop[2],
			    newop[3], newop[4], newop[5],
			    newop[6], newop[7], newop[8]));

  return true;
}


/* Apply the given unary intrinsic to OPERANDS[1] and store it on
   OPERANDS[0].  Report an error if the instruction could not
   be synthesized.  OPERANDS[1] is a register_operand.  For sign
   and zero extensions, it may be smaller than SImode.  */

bool
mep_expand_unary_intrinsic (int ATTRIBUTE_UNUSED intrinsic,
			    rtx * operands ATTRIBUTE_UNUSED)
{
  return false;
}


/* Likewise, but apply a binary operation to OPERANDS[1] and
   OPERANDS[2].  OPERANDS[1] is a register_operand, OPERANDS[2]
   can be a general_operand.

   IMMEDIATE and IMMEDIATE3 are intrinsics that take an immediate
   third operand.  REG and REG3 take register operands only.  */

bool
mep_expand_binary_intrinsic (int ATTRIBUTE_UNUSED immediate,
			     int ATTRIBUTE_UNUSED immediate3,
			     int ATTRIBUTE_UNUSED reg,
			     int ATTRIBUTE_UNUSED reg3,
			     rtx * operands ATTRIBUTE_UNUSED)
{
  return false;
}

static bool
mep_rtx_cost (rtx x, int code, int outer_code ATTRIBUTE_UNUSED, int *total, bool ATTRIBUTE_UNUSED speed_t)
{
  switch (code)
    {
    case CONST_INT:
      if (INTVAL (x) >= -128 && INTVAL (x) < 127)
	*total = 0;
      else if (INTVAL (x) >= -32768 && INTVAL (x) < 65536)
	*total = 1;
      else
	*total = 3;
      return true;

    case SYMBOL_REF:
      *total = optimize_size ? COSTS_N_INSNS (0) : COSTS_N_INSNS (1);
      return true;

    case MULT:							
      *total = (GET_CODE (XEXP (x, 1)) == CONST_INT
		? COSTS_N_INSNS (3)
		: COSTS_N_INSNS (2));
      return true;
    }
  return false;
}

static int
mep_address_cost (rtx addr ATTRIBUTE_UNUSED, bool ATTRIBUTE_UNUSED speed_p)
{
  return 1;
}

static bool
mep_handle_option (size_t code,
		   const char *arg ATTRIBUTE_UNUSED,
		   int value ATTRIBUTE_UNUSED)
{
  int i;

  switch (code)
    {
    case OPT_mall_opts:
      target_flags |= MEP_ALL_OPTS;
      break;

    case OPT_mno_opts:
      target_flags &= ~ MEP_ALL_OPTS;
      break;

    case OPT_mcop64:
      target_flags |= MASK_COP;
      target_flags |= MASK_64BIT_CR_REGS;
      break;

    case OPT_mtiny_:
      option_mtiny_specified = 1;

    case OPT_mivc2:
      target_flags |= MASK_COP;
      target_flags |= MASK_64BIT_CR_REGS;
      target_flags |= MASK_VLIW;
      target_flags |= MASK_OPT_VL64;
      target_flags |= MASK_IVC2;

      for (i=0; i<32; i++)
	fixed_regs[i+48] = 0;
      for (i=0; i<32; i++)
	call_used_regs[i+48] = 1;
      for (i=6; i<8; i++)
	call_used_regs[i+48] = 0;

#define RN(n,s) reg_names[FIRST_CCR_REGNO + n] = s
      RN (0, "$csar0");
      RN (1, "$cc");
      RN (4, "$cofr0");
      RN (5, "$cofr1");
      RN (6, "$cofa0");
      RN (7, "$cofa1");
      RN (15, "$csar1");

      RN (16, "$acc0_0");
      RN (17, "$acc0_1");
      RN (18, "$acc0_2");
      RN (19, "$acc0_3");
      RN (20, "$acc0_4");
      RN (21, "$acc0_5");
      RN (22, "$acc0_6");
      RN (23, "$acc0_7");

      RN (24, "$acc1_0");
      RN (25, "$acc1_1");
      RN (26, "$acc1_2");
      RN (27, "$acc1_3");
      RN (28, "$acc1_4");
      RN (29, "$acc1_5");
      RN (30, "$acc1_6");
      RN (31, "$acc1_7");
#undef RN

      break;

    default:
      break;
    }
  return TRUE;
}

static void
mep_asm_init_sections (void)
{
  based_section
    = get_unnamed_section (SECTION_WRITE, output_section_asm_op,
			   "\t.section .based,\"aw\"");

  tinybss_section
    = get_unnamed_section (SECTION_WRITE | SECTION_BSS, output_section_asm_op,
			   "\t.section .sbss,\"aw\"");

  sdata_section
    = get_unnamed_section (SECTION_WRITE, output_section_asm_op,
			   "\t.section .sdata,\"aw\",@progbits");

  far_section
    = get_unnamed_section (SECTION_WRITE, output_section_asm_op,
			   "\t.section .far,\"aw\"");

  farbss_section
    = get_unnamed_section (SECTION_WRITE | SECTION_BSS, output_section_asm_op,
			   "\t.section .farbss,\"aw\"");

  frodata_section
    = get_unnamed_section (0, output_section_asm_op,
			   "\t.section .frodata,\"a\"");

  srodata_section
    = get_unnamed_section (0, output_section_asm_op,
			   "\t.section .srodata,\"a\"");

  vtext_section
    = get_unnamed_section (SECTION_CODE | SECTION_MEP_VLIW, output_section_asm_op,
			   "\t.section .vtext,\"axv\"\n\t.vliw");

  vftext_section
    = get_unnamed_section (SECTION_CODE | SECTION_MEP_VLIW, output_section_asm_op,
			   "\t.section .vftext,\"axv\"\n\t.vliw");

  ftext_section
    = get_unnamed_section (SECTION_CODE, output_section_asm_op,
			   "\t.section .ftext,\"ax\"\n\t.core");

}

/* Initialize the GCC target structure.  */

#undef  TARGET_ASM_FUNCTION_PROLOGUE
#define TARGET_ASM_FUNCTION_PROLOGUE	mep_start_function
#undef  TARGET_ATTRIBUTE_TABLE
#define TARGET_ATTRIBUTE_TABLE		mep_attribute_table
#undef  TARGET_COMP_TYPE_ATTRIBUTES
#define TARGET_COMP_TYPE_ATTRIBUTES	mep_comp_type_attributes
#undef  TARGET_INSERT_ATTRIBUTES
#define TARGET_INSERT_ATTRIBUTES	mep_insert_attributes
#undef  TARGET_FUNCTION_ATTRIBUTE_INLINABLE_P
#define TARGET_FUNCTION_ATTRIBUTE_INLINABLE_P	mep_function_attribute_inlinable_p
#undef  TARGET_CAN_INLINE_P
#define TARGET_CAN_INLINE_P		mep_can_inline_p
#undef  TARGET_SECTION_TYPE_FLAGS
#define TARGET_SECTION_TYPE_FLAGS	mep_section_type_flags
#undef  TARGET_ASM_NAMED_SECTION
#define TARGET_ASM_NAMED_SECTION	mep_asm_named_section
#undef  TARGET_INIT_BUILTINS
#define TARGET_INIT_BUILTINS		mep_init_builtins
#undef  TARGET_EXPAND_BUILTIN
#define TARGET_EXPAND_BUILTIN		mep_expand_builtin
#undef  TARGET_SCHED_ADJUST_COST
#define TARGET_SCHED_ADJUST_COST	mep_adjust_cost
#undef  TARGET_SCHED_ISSUE_RATE
#define TARGET_SCHED_ISSUE_RATE		mep_issue_rate
#undef  TARGET_SCHED_REORDER
#define TARGET_SCHED_REORDER		mep_sched_reorder
#undef  TARGET_STRIP_NAME_ENCODING
#define TARGET_STRIP_NAME_ENCODING	mep_strip_name_encoding
#undef  TARGET_ASM_SELECT_SECTION
#define TARGET_ASM_SELECT_SECTION	mep_select_section
#undef  TARGET_ASM_UNIQUE_SECTION
#define TARGET_ASM_UNIQUE_SECTION	mep_unique_section
#undef  TARGET_ENCODE_SECTION_INFO
#define TARGET_ENCODE_SECTION_INFO	mep_encode_section_info
#undef  TARGET_FUNCTION_OK_FOR_SIBCALL
#define TARGET_FUNCTION_OK_FOR_SIBCALL	mep_function_ok_for_sibcall
#undef  TARGET_RTX_COSTS
#define TARGET_RTX_COSTS		mep_rtx_cost
#undef  TARGET_ADDRESS_COST
#define TARGET_ADDRESS_COST 		mep_address_cost
#undef  TARGET_MACHINE_DEPENDENT_REORG
#define TARGET_MACHINE_DEPENDENT_REORG  mep_reorg
#undef  TARGET_SETUP_INCOMING_VARARGS
#define TARGET_SETUP_INCOMING_VARARGS	mep_setup_incoming_varargs
#undef  TARGET_PASS_BY_REFERENCE
#define TARGET_PASS_BY_REFERENCE        mep_pass_by_reference
#undef  TARGET_FUNCTION_ARG
#define TARGET_FUNCTION_ARG             mep_function_arg
#undef  TARGET_FUNCTION_ARG_ADVANCE
#define TARGET_FUNCTION_ARG_ADVANCE     mep_function_arg_advance
#undef  TARGET_VECTOR_MODE_SUPPORTED_P
#define TARGET_VECTOR_MODE_SUPPORTED_P	mep_vector_mode_supported_p
#undef  TARGET_HANDLE_OPTION
#define TARGET_HANDLE_OPTION            mep_handle_option
#undef  TARGET_OPTION_OVERRIDE
#define TARGET_OPTION_OVERRIDE		mep_option_override
#undef  TARGET_OPTION_OPTIMIZATION_TABLE
#define TARGET_OPTION_OPTIMIZATION_TABLE	mep_option_optimization_table
#undef  TARGET_DEFAULT_TARGET_FLAGS
#define TARGET_DEFAULT_TARGET_FLAGS	TARGET_DEFAULT
#undef  TARGET_ALLOCATE_INITIAL_VALUE
#define TARGET_ALLOCATE_INITIAL_VALUE   mep_allocate_initial_value
#undef  TARGET_ASM_INIT_SECTIONS
#define TARGET_ASM_INIT_SECTIONS 	mep_asm_init_sections
#undef  TARGET_RETURN_IN_MEMORY
#define TARGET_RETURN_IN_MEMORY		mep_return_in_memory
#undef  TARGET_NARROW_VOLATILE_BITFIELD
#define TARGET_NARROW_VOLATILE_BITFIELD mep_narrow_volatile_bitfield
#undef	TARGET_EXPAND_BUILTIN_SAVEREGS
#define	TARGET_EXPAND_BUILTIN_SAVEREGS	mep_expand_builtin_saveregs
#undef  TARGET_BUILD_BUILTIN_VA_LIST
#define TARGET_BUILD_BUILTIN_VA_LIST	mep_build_builtin_va_list
#undef  TARGET_EXPAND_BUILTIN_VA_START
#define TARGET_EXPAND_BUILTIN_VA_START	mep_expand_va_start
#undef	TARGET_GIMPLIFY_VA_ARG_EXPR
#define	TARGET_GIMPLIFY_VA_ARG_EXPR	mep_gimplify_va_arg_expr
#undef  TARGET_CAN_ELIMINATE
#define TARGET_CAN_ELIMINATE            mep_can_eliminate
#undef  TARGET_CONDITIONAL_REGISTER_USAGE
#define TARGET_CONDITIONAL_REGISTER_USAGE	mep_conditional_register_usage
#undef  TARGET_TRAMPOLINE_INIT
#define TARGET_TRAMPOLINE_INIT		mep_trampoline_init

struct gcc_target targetm = TARGET_INITIALIZER;

#include "gt-mep.h"