text: /* Target definitions for the MorphoRISC1
     Copyright (C) 2005 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 2, 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 COPYING.  If not, write to the Free
     Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
     02110-1301, USA.  */
  
  #include "config.h"
  #include "system.h"
  #include "coretypes.h"
  #include "tm.h"
  #include "rtl.h"
  #include "regs.h"
  #include "hard-reg-set.h"
  #include "real.h"
  #include "insn-config.h"
  #include "conditions.h"
  #include "insn-attr.h"
  #include "recog.h"
  #include "toplev.h"
  #include "output.h"
  #include "integrate.h"
  #include "tree.h"
  #include "function.h"
  #include "expr.h"
  #include "optabs.h"
  #include "libfuncs.h"
  #include "flags.h"
  #include "tm_p.h"
  #include "ggc.h"
  #include "insn-flags.h"
  #include "obstack.h"
  #include "except.h"
  #include "target.h"
  #include "target-def.h"
  #include "basic-block.h"
  
  /* Frame pointer register mask.  */
  #define FP_MASK		 	 (1 << (GPR_FP))
  
  /* Link register mask.  */
  #define LINK_MASK	 	 (1 << (GPR_LINK))
  
  /* Given a SIZE in bytes, advance to the next word.  */
  #define ROUND_ADVANCE(SIZE) (((SIZE) + UNITS_PER_WORD - 1) / UNITS_PER_WORD)
  
  /* A C structure for machine-specific, per-function data.
     This is added to the cfun structure.  */
  struct machine_function GTY(())
  {
    /* Flags if __builtin_return_address (n) with n >= 1 was used.  */
    int ra_needs_full_frame;
    struct rtx_def * eh_stack_adjust;
    int interrupt_handler;
    int has_loops;
  };
  
  /* Define the information needed to generate branch and scc insns.
     This is stored from the compare operation.  */
  struct rtx_def * mt_compare_op0;
  struct rtx_def * mt_compare_op1;
  
  /* Current frame information calculated by compute_frame_size.  */
  struct mt_frame_info current_frame_info;
  
  /* Zero structure to initialize current_frame_info.  */
  struct mt_frame_info zero_frame_info;
  
  /* mt doesn't have unsigned compares need a library call for this.  */
  struct rtx_def * mt_ucmpsi3_libcall;
  
  static int mt_flag_delayed_branch;
  
  
  static rtx
  mt_struct_value_rtx (tree fndecl ATTRIBUTE_UNUSED,
  			 int incoming ATTRIBUTE_UNUSED)
  {
    return gen_rtx_REG (Pmode, RETVAL_REGNUM);
  }
  
  /* Implement RETURN_ADDR_RTX.  */
  rtx
  mt_return_addr_rtx (int count)
  {
    if (count != 0)
      return NULL_RTX;
  
    return get_hard_reg_initial_val (Pmode, GPR_LINK);
  }
  
  /* The following variable value indicates the number of nops required
     between the current instruction and the next instruction to avoid
     any pipeline hazards.  */
  static int mt_nops_required = 0;
  static const char * mt_nop_reasons = "";
  
  /* Implement ASM_OUTPUT_OPCODE.  */
  const char *
  mt_asm_output_opcode (FILE *f ATTRIBUTE_UNUSED, const char *ptr)
  {
    if (mt_nops_required)
      fprintf (f, ";# need %d nops because of %s\n\t",
  	     mt_nops_required, mt_nop_reasons);
    
    while (mt_nops_required)
      {
        fprintf (f, "nop\n\t");
        -- mt_nops_required;
      }
    
    return ptr;
  }
  
  /* Given an insn, return whether it's a memory operation or a branch
     operation, otherwise return TYPE_ARITH.  */
  static enum attr_type
  mt_get_attr_type (rtx complete_insn)
  {
    rtx insn = PATTERN (complete_insn);
  
    if (JUMP_P (complete_insn))
      return TYPE_BRANCH;
    if (CALL_P (complete_insn))
      return TYPE_BRANCH;
  
    if (GET_CODE (insn) != SET)
      return TYPE_ARITH;
  
    if (SET_DEST (insn) == pc_rtx)
      return TYPE_BRANCH;
  
    if (GET_CODE (SET_DEST (insn)) == MEM)
      return TYPE_STORE;
  
    if (GET_CODE (SET_SRC (insn)) == MEM)
      return TYPE_LOAD;
    
    return TYPE_ARITH;
  }
  
  /* A helper routine for insn_dependent_p called through note_stores.  */
  
  static void
  insn_dependent_p_1 (rtx x, 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 bool
  insn_dependent_p (rtx x, rtx y)
  {
    rtx tmp;
  
    if (! INSN_P (x) || ! INSN_P (y))
      return 0;
  
    tmp = PATTERN (y);
    note_stores (PATTERN (x), insn_dependent_p_1, &tmp);
    if (tmp == NULL_RTX)
      return true;
  
    tmp = PATTERN (x);
    note_stores (PATTERN (y), insn_dependent_p_1, &tmp);
    return (tmp == NULL_RTX);
  }
  
  
  /* Return true if anything in insn X is true dependent on anything in
     insn Y.  */
  static bool
  insn_true_dependent_p (rtx x, rtx y)
  {
    rtx tmp;
  
    if (! INSN_P (x) || ! INSN_P (y))
      return 0;
  
    tmp = PATTERN (y);
    note_stores (PATTERN (x), insn_dependent_p_1, &tmp);
    return (tmp == NULL_RTX);
  }
  
  /* The following determines the number of nops that need to be
     inserted between the previous instructions and current instruction
     to avoid pipeline hazards on the mt processor.  Remember that
     the function is not called for asm insns.  */
  
  void
  mt_final_prescan_insn (rtx   insn,
  			rtx * opvec ATTRIBUTE_UNUSED,
  			int   noperands ATTRIBUTE_UNUSED)
  {
    rtx prev_i;
    enum attr_type prev_attr;
  
    mt_nops_required = 0;
    mt_nop_reasons = "";
  
    /* ms2 constraints are dealt with in reorg.  */
    if (TARGET_MS2)
      return;
    
    /* Only worry about real instructions.  */
    if (! INSN_P (insn))
      return;
  
    /* Find the previous real instructions.  */
    for (prev_i = PREV_INSN (insn);
         prev_i != NULL
  	 && (! INSN_P (prev_i)
  	     || GET_CODE (PATTERN (prev_i)) == USE
  	     || GET_CODE (PATTERN (prev_i)) == CLOBBER);
         prev_i = PREV_INSN (prev_i))
      {
        /* If we meet a barrier, there is no flow through here.  */
        if (BARRIER_P (prev_i))
  	return;
      }
    
    /* If there isn't one then there is nothing that we need do.  */
    if (prev_i == NULL || ! INSN_P (prev_i))
      return;
  
    prev_attr = mt_get_attr_type (prev_i);
    
    /* Delayed branch slots already taken care of by delay branch scheduling.  */
    if (prev_attr == TYPE_BRANCH)
      return;
  
    switch (mt_get_attr_type (insn))
      {
      case TYPE_LOAD:
      case TYPE_STORE:
        /* Avoid consecutive memory operation.  */
        if  ((prev_attr == TYPE_LOAD || prev_attr == TYPE_STORE)
  	   && TARGET_MS1_64_001)
  	{
  	  mt_nops_required = 1;
  	  mt_nop_reasons = "consecutive mem ops";
  	}
        /* Drop through.  */
  
      case TYPE_ARITH:
      case TYPE_COMPLEX:
        /* One cycle of delay is required between load
  	 and the dependent arithmetic instruction.  */
        if (prev_attr == TYPE_LOAD
  	  && insn_true_dependent_p (prev_i, insn))
  	{
  	  mt_nops_required = 1;
  	  mt_nop_reasons = "load->arith dependency delay";
  	}
        break;
  
      case TYPE_BRANCH:
        if (insn_dependent_p (prev_i, insn))
  	{
  	  if (prev_attr == TYPE_ARITH && TARGET_MS1_64_001)
  	    {
  	      /* One cycle of delay between arith
  		 instructions and branch dependent on arith.  */
  	      mt_nops_required = 1;
  	      mt_nop_reasons = "arith->branch dependency delay";
  	    }
  	  else if (prev_attr == TYPE_LOAD)
  	    {
  	      /* Two cycles of delay are required
  		 between load and dependent branch.  */
  	      if (TARGET_MS1_64_001)
  		mt_nops_required = 2;
  	      else
  		mt_nops_required = 1;
  	      mt_nop_reasons = "load->branch dependency delay";
  	    }
  	}
        break;
  
      default:
        fatal_insn ("mt_final_prescan_insn, invalid insn #1", insn);
        break;
      }
  }
  
  /* Print debugging information for a frame.  */
  static void
  mt_debug_stack (struct mt_frame_info * info)
  {
    int regno;
  
    if (!info)
      {
        error ("info pointer NULL");
        gcc_unreachable ();
      }
  
    fprintf (stderr, "\nStack information for function %s:\n",
  	   ((current_function_decl && DECL_NAME (current_function_decl))
  	    ? IDENTIFIER_POINTER (DECL_NAME (current_function_decl))
  	    : "<unknown>"));
  
    fprintf (stderr, "\ttotal_size       = %d\n", info->total_size);
    fprintf (stderr, "\tpretend_size     = %d\n", info->pretend_size);
    fprintf (stderr, "\targs_size        = %d\n", info->args_size);
    fprintf (stderr, "\textra_size       = %d\n", info->extra_size);
    fprintf (stderr, "\treg_size         = %d\n", info->reg_size);
    fprintf (stderr, "\tvar_size         = %d\n", info->var_size);
    fprintf (stderr, "\tframe_size       = %d\n", info->frame_size);
    fprintf (stderr, "\treg_mask         = 0x%x\n", info->reg_mask);
    fprintf (stderr, "\tsave_fp          = %d\n", info->save_fp);
    fprintf (stderr, "\tsave_lr          = %d\n", info->save_lr);
    fprintf (stderr, "\tinitialized      = %d\n", info->initialized);
    fprintf (stderr, "\tsaved registers =");
  
    /* Print out reg_mask in a more readable format.  */
    for (regno = GPR_R0; regno <= GPR_LAST; regno++)
      if ( (1 << regno) & info->reg_mask)
        fprintf (stderr, " %s", reg_names[regno]);
  
    putc ('\n', stderr);
    fflush (stderr);
  }
  
  /* Print a memory address as an operand to reference that memory location.  */
  
  static void
  mt_print_operand_simple_address (FILE * file, rtx addr)
  {
    if (!addr)
      error ("PRINT_OPERAND_ADDRESS, null pointer");
  
    else
      switch (GET_CODE (addr))
        {
        case REG:
  	fprintf (file, "%s, #0", reg_names [REGNO (addr)]);
  	break;
  	
        case PLUS:
  	{
  	  rtx reg = 0;
  	  rtx offset = 0;
  	  rtx arg0 = XEXP (addr, 0);
  	  rtx arg1 = XEXP (addr, 1);
  
  	  if (GET_CODE (arg0) == REG)
  	    {
  	      reg = arg0;
  	      offset = arg1;
  	      if (GET_CODE (offset) == REG)
  		fatal_insn ("PRINT_OPERAND_ADDRESS, 2 regs", addr);
  	    }
  
  	  else if (GET_CODE (arg1) == REG)
  	      reg = arg1, offset = arg0;
  	  else if (CONSTANT_P (arg0) && CONSTANT_P (arg1))
  	    {
  	      fprintf (file, "%s, #", reg_names [GPR_R0]);
  	      output_addr_const (file, addr);
  	      break;
  	    }
  	  fprintf (file, "%s, #", reg_names [REGNO (reg)]);
  	  output_addr_const (file, offset);
  	  break;
  	}
  
        case LABEL_REF:
        case SYMBOL_REF:
        case CONST_INT:
        case CONST:
  	output_addr_const (file, addr);
  	break;
  
        default:
  	fatal_insn ("PRINT_OPERAND_ADDRESS, invalid insn #1", addr);
  	break;
        }
  }
  
  /* Implement PRINT_OPERAND_ADDRESS.  */
  void
  mt_print_operand_address (FILE * file, rtx addr)
  {
    if (GET_CODE (addr) == AND
        && GET_CODE (XEXP (addr, 1)) == CONST_INT
        && INTVAL (XEXP (addr, 1)) == -3)
      mt_print_operand_simple_address (file, XEXP (addr, 0));
    else
      mt_print_operand_simple_address (file, addr);
  }
  
  /* Implement PRINT_OPERAND.  */
  void
  mt_print_operand (FILE * file, rtx x, int code)
  {
    switch (code)
      {
      case '#':
        /* Output a nop if there's nothing for the delay slot.  */
        if (dbr_sequence_length () == 0)
  	fputs ("\n\tnop", file);
        return;
        
      case 'H': 
        fprintf(file, "#%%hi16(");
        output_addr_const (file, x);
        fprintf(file, ")");
        return;
        
      case 'L': 
        fprintf(file, "#%%lo16(");
        output_addr_const (file, x);
        fprintf(file, ")");
        return;
  
      case 'N': 
        fprintf(file, "#%ld", ~INTVAL (x));
        return;
  
      case 'z':
        if (GET_CODE (x) == CONST_INT && INTVAL (x) == 0)
  	{
  	  fputs (reg_names[GPR_R0], file);
  	  return;
  	}
  
      case 0:
        /* Handled below.  */
        break;
  
      default:
        /* output_operand_lossage ("mt_print_operand: unknown code"); */
        fprintf (file, "unknown code");
        return;
      }
  
    switch (GET_CODE (x))
      {
      case REG:
        fputs (reg_names [REGNO (x)], file);
        break;
  
      case CONST:
      case CONST_INT:
        fprintf(file, "#%ld", INTVAL (x));
        break;
  
      case MEM:
        mt_print_operand_address(file, XEXP (x,0));
        break;
  
      case LABEL_REF:
      case SYMBOL_REF:
        output_addr_const (file, x);
        break;
        
      default:
        fprintf(file, "Uknown code: %d", GET_CODE (x));
        break;
      }
  
    return;
  }
  
  /* Implement INIT_CUMULATIVE_ARGS.  */
  void
  mt_init_cumulative_args (CUMULATIVE_ARGS * cum, tree fntype, rtx libname,
  			 tree fndecl ATTRIBUTE_UNUSED, int incoming)
  {
    *cum = 0;
  
    if (TARGET_DEBUG_ARG)
      {
        fprintf (stderr, "\nmt_init_cumulative_args:");
  
        if (incoming)
  	fputs (" incoming", stderr);
  
        if (fntype)
  	{
  	  tree ret_type = TREE_TYPE (fntype);
  	  fprintf (stderr, " return = %s,",
  		   tree_code_name[ (int)TREE_CODE (ret_type) ]);
  	}
  
        if (libname && GET_CODE (libname) == SYMBOL_REF)
  	fprintf (stderr, " libname = %s", XSTR (libname, 0));
  
        if (cfun->returns_struct)
  	fprintf (stderr, " return-struct");
  
        putc ('\n', stderr);
      }
  }
  
  /* Compute the slot number to pass an argument in.
     Returns the slot number or -1 if passing on the stack.
  
     CUM is a variable of type CUMULATIVE_ARGS which gives info about
      the preceding args and about the function being called.
     MODE is the argument's machine mode.
     TYPE is the data type of the argument (as a tree).
      This is null for libcalls where that information may
      not be available.
     NAMED is nonzero if this argument is a named parameter
      (otherwise it is an extra parameter matching an ellipsis).
     INCOMING_P is zero for FUNCTION_ARG, nonzero for FUNCTION_INCOMING_ARG.
     *PREGNO records the register number to use if scalar type.  */
  
  static int
  mt_function_arg_slotno (const CUMULATIVE_ARGS * cum,
  			enum machine_mode mode,
  			tree type,
  			int named ATTRIBUTE_UNUSED,
  			int incoming_p ATTRIBUTE_UNUSED,
  			int * pregno)
  {
    int regbase = FIRST_ARG_REGNUM;
    int slotno  = * cum;
  
    if (mode == VOIDmode || targetm.calls.must_pass_in_stack (mode, type))
      return -1;
  
    if (slotno >= MT_NUM_ARG_REGS)
      return -1;
  
    * pregno = regbase + slotno;
  
    return slotno;
  }
  
  /* Implement FUNCTION_ARG.  */
  rtx
  mt_function_arg (const CUMULATIVE_ARGS * cum,
  		 enum machine_mode mode,
  		 tree type,
  		 int named,
  		 int incoming_p)
  {
    int slotno, regno;
    rtx reg;
  
    slotno = mt_function_arg_slotno (cum, mode, type, named, incoming_p, &regno);
  
    if (slotno == -1)
      reg = NULL_RTX;
    else
      reg = gen_rtx_REG (mode, regno);
  
    return reg;
  }
  
  /* Implement FUNCTION_ARG_ADVANCE.  */
  void
  mt_function_arg_advance (CUMULATIVE_ARGS * cum,
  			 enum machine_mode mode,
  			 tree type ATTRIBUTE_UNUSED,
  			 int named)
  {
    int slotno, regno;
  
    /* We pass 0 for incoming_p here, it doesn't matter.  */
    slotno = mt_function_arg_slotno (cum, mode, type, named, 0, &regno);
  
    * cum += (mode != BLKmode
  	    ? ROUND_ADVANCE (GET_MODE_SIZE (mode))
  	    : ROUND_ADVANCE (int_size_in_bytes (type)));
  
    if (TARGET_DEBUG_ARG)
      fprintf (stderr,
  	     "mt_function_arg_advance: words = %2d, mode = %4s, named = %d, size = %3d\n",
  	     *cum, GET_MODE_NAME (mode), named, 
  	     (*cum) * UNITS_PER_WORD);
  }
  
  /* Implement hook TARGET_ARG_PARTIAL_BYTES.
  
     Returns the number of bytes at the beginning of an argument that
     must be put in registers.  The value must be zero for arguments
     that are passed entirely in registers or that are entirely pushed
     on the stack.  */
  static int
  mt_arg_partial_bytes (CUMULATIVE_ARGS * pcum,
  		       enum machine_mode mode,
  		       tree type,
  		       bool named ATTRIBUTE_UNUSED)
  {
    int cum = * pcum;
    int words;
  
    if (mode == BLKmode)
      words = ((int_size_in_bytes (type) + UNITS_PER_WORD - 1)
  	     / UNITS_PER_WORD);
    else
      words = (GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
  
    if (! targetm.calls.pass_by_reference (&cum, mode, type, named)
        && cum < MT_NUM_ARG_REGS
        && (cum + words) > MT_NUM_ARG_REGS)
      {
        int bytes = (MT_NUM_ARG_REGS - cum) * UNITS_PER_WORD; 
  
        if (TARGET_DEBUG)
  	fprintf (stderr, "function_arg_partial_nregs = %d\n", bytes);
        return bytes;
      }
  
    return 0;
  }
  
  
  /* Implement TARGET_PASS_BY_REFERENCE hook.  */
  static bool
  mt_pass_by_reference (CUMULATIVE_ARGS * cum ATTRIBUTE_UNUSED,
  		       enum machine_mode mode ATTRIBUTE_UNUSED,
  		       tree type,
  		       bool named ATTRIBUTE_UNUSED)
  {
    return (type && int_size_in_bytes (type) > 4 * UNITS_PER_WORD);
  }
  
  /* Implement FUNCTION_ARG_BOUNDARY.  */
  int
  mt_function_arg_boundary (enum machine_mode mode ATTRIBUTE_UNUSED,
  			   tree type ATTRIBUTE_UNUSED)
  {
    return BITS_PER_WORD;
  }
  
  /* Implement REG_OK_FOR_BASE_P.  */
  int
  mt_reg_ok_for_base_p (rtx x, int strict)
  {
    if (strict)
      return  (((unsigned) REGNO (x)) < FIRST_PSEUDO_REGISTER);
    return 1;
  }
  
  /* Helper function of mt_legitimate_address_p.  Return true if XINSN
     is a simple address, otherwise false.  */
  static bool
  mt_legitimate_simple_address_p (enum machine_mode mode ATTRIBUTE_UNUSED,
  				rtx xinsn, int strict)
  {
    if (TARGET_DEBUG)						
      {									
        fprintf (stderr, "\n========== GO_IF_LEGITIMATE_ADDRESS, %sstrict\n",
  	       strict ? "" : "not ");
        debug_rtx (xinsn);
      }
  
    if (GET_CODE (xinsn) == REG && mt_reg_ok_for_base_p (xinsn, strict))
      return true;
  
    if (GET_CODE (xinsn) == PLUS
        && GET_CODE (XEXP (xinsn, 0)) == REG
        && mt_reg_ok_for_base_p (XEXP (xinsn, 0), strict)
        && GET_CODE (XEXP (xinsn, 1)) == CONST_INT
        && SMALL_INT (XEXP (xinsn, 1)))
      return true;
  
    return false;
  }
  
  
  /* Helper function of GO_IF_LEGITIMATE_ADDRESS.  Return nonzero if
     XINSN is a legitimate address on MT.  */
  int
  mt_legitimate_address_p (enum machine_mode mode, rtx xinsn, int strict)
  {
    if (mt_legitimate_simple_address_p (mode, xinsn, strict))
      return 1;
  
    if ((mode) == SImode
        && GET_CODE (xinsn) == AND
        && GET_CODE (XEXP (xinsn, 1)) == CONST_INT
        && INTVAL (XEXP (xinsn, 1)) == -3)
      return mt_legitimate_simple_address_p (mode, XEXP (xinsn, 0), strict);
    else
      return 0;
  }
  
  /* Return truth value of whether OP can be used as an operands where a
     register or 16 bit unsigned integer is needed.  */
  
  int
  uns_arith_operand (rtx op, enum machine_mode mode)
  {
    if (GET_CODE (op) == CONST_INT && SMALL_INT_UNSIGNED (op))
      return 1;
  
    return register_operand (op, mode);
  }
  
  /* Return truth value of whether OP can be used as an operands where a
     16 bit integer is needed.  */
  
  int
  arith_operand (rtx op, enum machine_mode mode)
  {
    if (GET_CODE (op) == CONST_INT && SMALL_INT (op))
      return 1;
  
    return register_operand (op, mode);
  }
  
  /* Return truth value of whether OP is a register or the constant 0.  */
  
  int
  reg_or_0_operand (rtx op, enum machine_mode mode)
  {
    switch (GET_CODE (op))
      {
      case CONST_INT:
        return INTVAL (op) == 0;
  
      case REG:
      case SUBREG:
        return register_operand (op, mode);
  
      default:
        break;
      }
  
    return 0;
  }
  
  /* Return truth value of whether OP is a constant that requires two
     loads to put in a register.  */
  
  int
  big_const_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
  {
    if (GET_CODE (op) == CONST_INT && CONST_OK_FOR_LETTER_P (INTVAL (op), 'M'))
      return 1;
  
    return 0;
  }
  
  /* Return truth value of whether OP is a constant that require only
     one load to put in a register.  */
  
  int
  single_const_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
  {
    if (big_const_operand (op, mode)
        || GET_CODE (op) == CONST
        || GET_CODE (op) == LABEL_REF
        || GET_CODE (op) == SYMBOL_REF)
      return 0;
  
    return 1;
  }
  
  /* True if the current function is an interrupt handler
     (either via #pragma or an attribute specification).  */
  int interrupt_handler;
  enum processor_type mt_cpu;
  
  static struct machine_function *
  mt_init_machine_status (void)
  {
    struct machine_function *f;
  
    f = ggc_alloc_cleared (sizeof (struct machine_function));
  
    return f;
  }
  
  /* Implement OVERRIDE_OPTIONS.  */
  void
  mt_override_options (void)
  {
    if (mt_cpu_string != NULL)
      {
        if (!strcmp (mt_cpu_string, "ms1-64-001"))
  	mt_cpu = PROCESSOR_MS1_64_001;
        else if (!strcmp (mt_cpu_string, "ms1-16-002"))
  	mt_cpu = PROCESSOR_MS1_16_002;
        else if  (!strcmp (mt_cpu_string, "ms1-16-003"))
  	mt_cpu = PROCESSOR_MS1_16_003;
        else if (!strcmp (mt_cpu_string, "ms2"))
  	mt_cpu = PROCESSOR_MS2;
        else
  	error ("bad value (%s) for -march= switch", mt_cpu_string);
      }
    else
      mt_cpu = PROCESSOR_MS1_16_002;
  
    if (flag_exceptions)
      {
        flag_omit_frame_pointer = 0;
        flag_gcse = 0;
      }
  
    /* We do delayed branch filling in machine dependent reorg */
    mt_flag_delayed_branch = flag_delayed_branch;
    flag_delayed_branch = 0;
  
    init_machine_status = mt_init_machine_status;
  }
  
  /* Do what is necessary for `va_start'.  We look at the current function
     to determine if stdarg or varargs is used and return the address of the
     first unnamed parameter.  */
  
  static void
  mt_setup_incoming_varargs (CUMULATIVE_ARGS *cum,
  			   enum machine_mode mode ATTRIBUTE_UNUSED,
  			   tree type ATTRIBUTE_UNUSED,
  			   int *pretend_size, int no_rtl)
  {
    int regno;
    int regs = MT_NUM_ARG_REGS - *cum;
    
    *pretend_size = regs < 0 ? 0 : GET_MODE_SIZE (SImode) * regs;
    
    if (no_rtl)
      return;
    
    for (regno = *cum; regno < MT_NUM_ARG_REGS; regno++)
      {
        rtx reg = gen_rtx_REG (SImode, FIRST_ARG_REGNUM + regno);
        rtx slot = gen_rtx_PLUS (Pmode,
  			       gen_rtx_REG (SImode, ARG_POINTER_REGNUM),
  			       GEN_INT (UNITS_PER_WORD * regno));
        
        emit_move_insn (gen_rtx_MEM (SImode, slot), reg);
      }
  }
  
  /* Returns the number of bytes offset between the frame pointer and the stack
     pointer for the current function.  SIZE is the number of bytes of space
     needed for local variables.  */
  
  unsigned int
  mt_compute_frame_size (int size)
  {
    int           regno;
    unsigned int  total_size;
    unsigned int  var_size;
    unsigned int  args_size;
    unsigned int  pretend_size;
    unsigned int  extra_size;
    unsigned int  reg_size;
    unsigned int  frame_size;
    unsigned int  reg_mask;
  
    var_size      = size;
    args_size     = current_function_outgoing_args_size;
    pretend_size  = current_function_pretend_args_size;
    extra_size    = FIRST_PARM_OFFSET (0);
    total_size    = extra_size + pretend_size + args_size + var_size;
    reg_size      = 0;
    reg_mask	= 0;
  
    /* Calculate space needed for registers.  */
    for (regno = GPR_R0; regno <= GPR_LAST; regno++)
      {
        if (MUST_SAVE_REGISTER (regno))
          {
            reg_size += UNITS_PER_WORD;
            reg_mask |= 1 << regno;
          }
      }
  
    current_frame_info.save_fp = (regs_ever_live [GPR_FP]
  				|| frame_pointer_needed
  				|| interrupt_handler);
    current_frame_info.save_lr = (regs_ever_live [GPR_LINK]
  				|| profile_flag
  				|| interrupt_handler);
   
    reg_size += (current_frame_info.save_fp + current_frame_info.save_lr)
                 * UNITS_PER_WORD;
    total_size += reg_size;
    total_size = ((total_size + 3) & ~3);
  
    frame_size = total_size;
  
    /* Save computed information.  */
    current_frame_info.pretend_size = pretend_size;
    current_frame_info.var_size     = var_size;
    current_frame_info.args_size    = args_size;
    current_frame_info.reg_size     = reg_size;
    current_frame_info.frame_size   = args_size + var_size;
    current_frame_info.total_size   = total_size;
    current_frame_info.extra_size   = extra_size;
    current_frame_info.reg_mask     = reg_mask;
    current_frame_info.initialized  = reload_completed;
   
    return total_size;
  }
  
  /* Emit code to save REG in stack offset pointed to by MEM.
     STACK_OFFSET is the offset from the SP where the save will happen.
     This function sets the REG_FRAME_RELATED_EXPR note accordingly.  */
  static void
  mt_emit_save_restore (enum save_direction direction,
  		      rtx reg, rtx mem, int stack_offset)
  {
    if (direction == FROM_PROCESSOR_TO_MEM)
      {
        rtx insn;
    
        insn = emit_move_insn (mem, reg);
        RTX_FRAME_RELATED_P (insn) = 1;
        REG_NOTES (insn)
  	= gen_rtx_EXPR_LIST
  	(REG_FRAME_RELATED_EXPR,
  	 gen_rtx_SET (VOIDmode,
  		      gen_rtx_MEM (SImode,
  				   gen_rtx_PLUS (SImode,
  						 stack_pointer_rtx,
  						 GEN_INT (stack_offset))),
  		      reg),
  	 REG_NOTES (insn));
      }
    else
      emit_move_insn (reg, mem);
  }
  
  
  /* Emit code to save the frame pointer in the prologue and restore
     frame pointer in epilogue.  */
  
  static void
  mt_emit_save_fp (enum save_direction direction,
  		  struct mt_frame_info info)
  {
    rtx base_reg;
    int reg_mask = info.reg_mask  & ~(FP_MASK | LINK_MASK);
    int offset = info.total_size;
    int stack_offset = info.total_size;
  
    /* If there is nothing to save, get out now.  */
    if (! info.save_fp && ! info.save_lr && ! reg_mask)
      return;
  
    /* If offset doesn't fit in a 15-bit signed integer,
       uses a scratch registers to get a smaller offset.  */
    if (CONST_OK_FOR_LETTER_P(offset, 'O'))
      base_reg = stack_pointer_rtx;
    else
      {
        /* Use the scratch register R9 that holds old stack pointer.  */
        base_reg = gen_rtx_REG (SImode, GPR_R9);
        offset = 0;
      }
  
    if (info.save_fp)
      {
        offset -= UNITS_PER_WORD;
        stack_offset -= UNITS_PER_WORD;
        mt_emit_save_restore
  	(direction, gen_rtx_REG (SImode, GPR_FP),
  	 gen_rtx_MEM (SImode,
  		      gen_rtx_PLUS (SImode, base_reg, GEN_INT (offset))),
  	 stack_offset);
      }
  }
  
  /* Emit code to save registers in the prologue and restore register
     in epilogue.  */
  
  static void
  mt_emit_save_regs (enum save_direction direction,
  		    struct mt_frame_info info)
  {
    rtx base_reg;
    int regno;
    int reg_mask = info.reg_mask  & ~(FP_MASK | LINK_MASK);
    int offset = info.total_size;
    int stack_offset = info.total_size;
  
    /* If there is nothing to save, get out now.  */
    if (! info.save_fp && ! info.save_lr && ! reg_mask)
      return;
  
    /* If offset doesn't fit in a 15-bit signed integer,
       uses a scratch registers to get a smaller offset.  */
    if (CONST_OK_FOR_LETTER_P(offset, 'O'))
      base_reg = stack_pointer_rtx;
    else
      {
        /* Use the scratch register R9 that holds old stack pointer.  */
        base_reg = gen_rtx_REG (SImode, GPR_R9);
        offset = 0;
      }
  
    if (info.save_fp)
      {
        /* This just records the space for it, the actual move generated in
  	 mt_emit_save_fp ().  */
        offset -= UNITS_PER_WORD;
        stack_offset -= UNITS_PER_WORD;
      }
  
    if (info.save_lr)
      {
        offset -= UNITS_PER_WORD;
        stack_offset -= UNITS_PER_WORD;
        mt_emit_save_restore
  	(direction, gen_rtx_REG (SImode, GPR_LINK), 
  	 gen_rtx_MEM (SImode,
  		      gen_rtx_PLUS (SImode, base_reg, GEN_INT (offset))),
  	 stack_offset);
      }
  
    /* Save any needed call-saved regs.  */
    for (regno = GPR_R0; regno <= GPR_LAST; regno++)
      {
        if ((reg_mask & (1 << regno)) != 0)
  	{
  	  offset -= UNITS_PER_WORD;
  	  stack_offset -= UNITS_PER_WORD;
  	  mt_emit_save_restore
  	    (direction, gen_rtx_REG (SImode, regno),
  	     gen_rtx_MEM (SImode,
  			  gen_rtx_PLUS (SImode, base_reg, GEN_INT (offset))),
  	     stack_offset);
  	}
      }
  }
  
  /* Return true if FUNC is a function with the 'interrupt' attribute.  */
  static bool
  mt_interrupt_function_p (tree func)
  {
    tree a;
  
    if (TREE_CODE (func) != FUNCTION_DECL)
      return false;
  
    a = lookup_attribute ("interrupt", DECL_ATTRIBUTES (func));
    return a != NULL_TREE;
  }
  
  /* Generate prologue code.  */
  void
  mt_expand_prologue (void)
  {
    rtx size_rtx, insn;
    unsigned int frame_size;
  
    if (mt_interrupt_function_p (current_function_decl))
      {
        interrupt_handler = 1;
        if (cfun->machine)
  	cfun->machine->interrupt_handler = 1;
      }
  
    mt_compute_frame_size (get_frame_size ());
  
    if (TARGET_DEBUG_STACK)
      mt_debug_stack (&current_frame_info);
  
    /* Compute size of stack adjustment.  */
    frame_size = current_frame_info.total_size;
  
    /* If offset doesn't fit in a 15-bit signed integer,
       uses a scratch registers to get a smaller offset.  */
    if (CONST_OK_FOR_LETTER_P(frame_size, 'O'))
      size_rtx = GEN_INT (frame_size);
    else
      {
        /* We do not have any scratch registers.  */
        gcc_assert (!interrupt_handler);
  
        size_rtx = gen_rtx_REG (SImode, GPR_R9);
        insn = emit_move_insn (size_rtx, GEN_INT (frame_size & 0xffff0000));
        insn = emit_insn (gen_iorsi3 (size_rtx, size_rtx,
  				    GEN_INT (frame_size & 0x0000ffff)));
      }
  
    /* Allocate stack for this frame.  */
    /* Make stack adjustment and use scratch register if constant too
       large to fit as immediate.  */
    if (frame_size)
      {
        insn = emit_insn (gen_subsi3 (stack_pointer_rtx,
  				 stack_pointer_rtx,
  				 size_rtx));
        RTX_FRAME_RELATED_P (insn) = 1;
        REG_NOTES (insn)
  	= gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR,
  			     gen_rtx_SET (VOIDmode,
  					  stack_pointer_rtx,
  					  gen_rtx_MINUS (SImode,
  							stack_pointer_rtx,
  							GEN_INT (frame_size))),
  			     REG_NOTES (insn));
      }
  
    /* Set R9 to point to old sp if required for access to register save
       area.  */
    if ( current_frame_info.reg_size != 0
         && !CONST_OK_FOR_LETTER_P (frame_size, 'O'))
        emit_insn (gen_addsi3 (size_rtx, size_rtx, stack_pointer_rtx));
    
    /* Save the frame pointer.  */
    mt_emit_save_fp (FROM_PROCESSOR_TO_MEM, current_frame_info);
  
    /* Now put the frame pointer into the frame pointer register.  */
    if (frame_pointer_needed)
      {
        insn = emit_move_insn (frame_pointer_rtx, stack_pointer_rtx);
        RTX_FRAME_RELATED_P (insn) = 1;
      }
  
    /* Save the registers.  */
    mt_emit_save_regs (FROM_PROCESSOR_TO_MEM, current_frame_info);
  
    /* If we are profiling, make sure no instructions are scheduled before
       the call to mcount.  */
    if (profile_flag)
      emit_insn (gen_blockage ());
  }
  
  /* Implement EPILOGUE_USES.  */
  int
  mt_epilogue_uses (int regno)
  {
    if (cfun->machine && cfun->machine->interrupt_handler && reload_completed)
      return 1;
    return regno == GPR_LINK;
  }
  
  /* Generate epilogue.  EH_MODE is NORMAL_EPILOGUE when generating a
     function epilogue, or EH_EPILOGUE when generating an EH
     epilogue.  */
  void
  mt_expand_epilogue (enum epilogue_type eh_mode)
  {
    rtx size_rtx, insn;
    unsigned frame_size;
  
    mt_compute_frame_size (get_frame_size ());
  
    if (TARGET_DEBUG_STACK)
      mt_debug_stack (& current_frame_info);
  
    /* Compute size of stack adjustment.  */
    frame_size = current_frame_info.total_size;
  
    /* If offset doesn't fit in a 15-bit signed integer,
       uses a scratch registers to get a smaller offset.  */
    if (CONST_OK_FOR_LETTER_P(frame_size, 'O'))
      size_rtx = GEN_INT (frame_size);
    else
      {
        /* We do not have any scratch registers.  */
        gcc_assert (!interrupt_handler);
  
        size_rtx = gen_rtx_REG (SImode, GPR_R9);
        insn = emit_move_insn (size_rtx, GEN_INT (frame_size & 0xffff0000));
        insn = emit_insn (gen_iorsi3 (size_rtx, size_rtx,
  				    GEN_INT (frame_size & 0x0000ffff)));
        /* Set R9 to point to old sp if required for access to register
  	 save area.  */
        emit_insn (gen_addsi3 (size_rtx, size_rtx, stack_pointer_rtx));
      }
  
    /* Restore sp if there was some possible change to it.  */
    if (frame_pointer_needed)
      insn = emit_move_insn (stack_pointer_rtx, frame_pointer_rtx);
  
    /* Restore the registers.  */
    mt_emit_save_fp (FROM_MEM_TO_PROCESSOR, current_frame_info);
    mt_emit_save_regs (FROM_MEM_TO_PROCESSOR, current_frame_info);
  
    /* Make stack adjustment and use scratch register if constant too
       large to fit as immediate.  */
    if (frame_size)
      {
        if (CONST_OK_FOR_LETTER_P(frame_size, 'O'))
  	/* Can handle this with simple add.  */
  	insn = emit_insn (gen_addsi3 (stack_pointer_rtx,
  				      stack_pointer_rtx,
  				      size_rtx));
        else
  	/* Scratch reg R9 has the old sp value.  */
  	insn = emit_move_insn (stack_pointer_rtx, 
  			       gen_rtx_REG (SImode, GPR_R9));
  
        REG_NOTES (insn)
  	= gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR,
  			     gen_rtx_SET (VOIDmode,
  					  stack_pointer_rtx,
  					  gen_rtx_PLUS (SImode,
  							stack_pointer_rtx,
  							GEN_INT (frame_size))),
  			     REG_NOTES (insn));
      }
  
    if (cfun->machine && cfun->machine->eh_stack_adjust != NULL_RTX)
      /* Perform the additional bump for __throw.  */
      emit_insn (gen_addsi3 (stack_pointer_rtx,
  			   stack_pointer_rtx,
  			   cfun->machine->eh_stack_adjust));
  
    /* Generate the appropriate return.  */
    if (eh_mode == EH_EPILOGUE)
      {
        emit_jump_insn (gen_eh_return_internal ());
        emit_barrier ();
      }
    else if (interrupt_handler)
      emit_jump_insn (gen_return_interrupt_internal ());
    else
      emit_jump_insn (gen_return_internal ());
  
    /* Reset state info for each function.  */
    interrupt_handler = 0;
    current_frame_info = zero_frame_info;
    if (cfun->machine)
      cfun->machine->eh_stack_adjust = NULL_RTX;
  }
  
  
  /* Generate code for the "eh_return" pattern.  */
  void
  mt_expand_eh_return (rtx * operands)
  {
    if (GET_CODE (operands[0]) != REG
        || REGNO (operands[0]) != EH_RETURN_STACKADJ_REGNO)
      {
        rtx sp = EH_RETURN_STACKADJ_RTX;
  
        emit_move_insn (sp, operands[0]);
        operands[0] = sp;
      }
  
    emit_insn (gen_eh_epilogue (operands[0]));
  }
  
  /* Generate code for the "eh_epilogue" pattern.  */
  void
  mt_emit_eh_epilogue (rtx * operands ATTRIBUTE_UNUSED)
  {
    cfun->machine->eh_stack_adjust = EH_RETURN_STACKADJ_RTX; /* operands[0]; */
    mt_expand_epilogue (EH_EPILOGUE);
  }
  
  /* Handle an "interrupt" attribute.  */
  static tree
  mt_handle_interrupt_attribute (tree * node,
  			  tree   name,
  			  tree   args  ATTRIBUTE_UNUSED,
  			  int    flags ATTRIBUTE_UNUSED,
  			  bool * no_add_attrs)
  {
    if (TREE_CODE (*node) != FUNCTION_DECL)
      {
        warning (OPT_Wattributes,
  	       "%qs attribute only applies to functions",
  	       IDENTIFIER_POINTER (name));
        *no_add_attrs = true;
      }
  
    return NULL_TREE;
  }
  
  /* Table of machine attributes.  */
  const struct attribute_spec mt_attribute_table[] =
  {
    /* name,        min, max, decl?, type?, func?, handler  */
    { "interrupt",  0,   0,   false, false, false, mt_handle_interrupt_attribute },
    { NULL,         0,   0,   false, false, false, NULL }
  };
  
  /* Implement INITIAL_ELIMINATION_OFFSET.  */
  int
  mt_initial_elimination_offset (int from, int to)
  {
    mt_compute_frame_size (get_frame_size ());
  
    if (from == FRAME_POINTER_REGNUM && to == STACK_POINTER_REGNUM)
      return 0;
  
    else if (from == ARG_POINTER_REGNUM && to == STACK_POINTER_REGNUM)
      return current_frame_info.total_size;
  
    else if (from == ARG_POINTER_REGNUM && to == FRAME_POINTER_REGNUM)
      return current_frame_info.total_size;
  
    else
      gcc_unreachable ();
  }
  
  /* Generate a compare for CODE.  Return a brand-new rtx that
     represents the result of the compare.  */
  
  static rtx
  mt_generate_compare (enum rtx_code code, rtx op0, rtx op1)
  {
    rtx scratch0, scratch1, const_scratch;
  
    switch (code)
      {
      case GTU:
      case LTU:
      case GEU:
      case LEU:
        /* Need to adjust ranges for faking unsigned compares.  */
        scratch0 = gen_reg_rtx (SImode);
        scratch1 = gen_reg_rtx (SImode);
        const_scratch = force_reg (SImode, GEN_INT(MT_MIN_INT));
        emit_insn (gen_addsi3 (scratch0, const_scratch, op0));
        emit_insn (gen_addsi3 (scratch1, const_scratch, op1));
        break;
      default:
        scratch0 = op0;
        scratch1 = op1;
        break;
      }
      
    /* Adjust compare operator to fake unsigned compares.  */
    switch (code)
      {
      case GTU:
        code = GT; break;
      case LTU:
        code = LT; break;
      case GEU:
        code = GE; break;
      case LEU:
        code = LE; break;
      default:
        /* do nothing */
        break;
      }
  
    /* Generate the actual compare.  */
    return gen_rtx_fmt_ee (code, VOIDmode, scratch0, scratch1);
  }
  
  /* Emit a branch of kind CODE to location LOC.  */
  
  void
  mt_emit_cbranch (enum rtx_code code, rtx loc, rtx op0, rtx op1)
  {
    rtx condition_rtx, loc_ref;
  
    if (! reg_or_0_operand (op0, SImode))
      op0 = copy_to_mode_reg (SImode, op0);
  
    if (! reg_or_0_operand (op1, SImode))
      op1 = copy_to_mode_reg (SImode, op1);
  
    condition_rtx = mt_generate_compare (code, op0, op1);
    loc_ref = gen_rtx_LABEL_REF (VOIDmode, loc);
    emit_jump_insn (gen_rtx_SET (VOIDmode, pc_rtx,
  			       gen_rtx_IF_THEN_ELSE (VOIDmode, condition_rtx,
  						     loc_ref, pc_rtx)));
  }
  
  /* Subfunction of the following function.  Update the flags of any MEM
     found in part of X.  */
  
  static void
  mt_set_memflags_1 (rtx x, int in_struct_p, int volatile_p)
  {
    int i;
  
    switch (GET_CODE (x))
      {
      case SEQUENCE:
      case PARALLEL:
        for (i = XVECLEN (x, 0) - 1; i >= 0; i--)
  	mt_set_memflags_1 (XVECEXP (x, 0, i), in_struct_p, volatile_p);
        break;
  
      case INSN:
        mt_set_memflags_1 (PATTERN (x), in_struct_p, volatile_p);
        break;
  
      case SET:
        mt_set_memflags_1 (SET_DEST (x), in_struct_p, volatile_p);
        mt_set_memflags_1 (SET_SRC (x), in_struct_p, volatile_p);
        break;
  
      case MEM:
        MEM_IN_STRUCT_P (x) = in_struct_p;
        MEM_VOLATILE_P (x) = volatile_p;
        /* Sadly, we cannot use alias sets because the extra aliasing
  	 produced by the AND interferes.  Given that two-byte quantities
  	 are the only thing we would be able to differentiate anyway,
  	 there does not seem to be any point in convoluting the early
  	 out of the alias check.  */
        /* set_mem_alias_set (x, alias_set); */
        break;
  
      default:
        break;
      }
  }
  
  /* Look for any MEMs in the current sequence of insns and set the
     in-struct, unchanging, and volatile flags from the flags in REF.
     If REF is not a MEM, don't do anything.  */
  
  void
  mt_set_memflags (rtx ref)
  {
    rtx insn;
    int in_struct_p, volatile_p;
  
    if (GET_CODE (ref) != MEM)
      return;
  
    in_struct_p = MEM_IN_STRUCT_P (ref);
    volatile_p = MEM_VOLATILE_P (ref);
  
    /* This is only called from mt.md, after having had something 
       generated from one of the insn patterns.  So if everything is
       zero, the pattern is already up-to-date.  */
    if (! in_struct_p && ! volatile_p)
      return;
  
    for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
      mt_set_memflags_1 (insn, in_struct_p, volatile_p);
  }
  
  /* Implement SECONDARY_RELOAD_CLASS.  */
  enum reg_class
  mt_secondary_reload_class (enum reg_class class ATTRIBUTE_UNUSED,
  			    enum machine_mode mode,
  			    rtx x)
  {
    if ((mode == QImode && (!TARGET_BYTE_ACCESS)) || mode == HImode)
      {
        if (GET_CODE (x) == MEM
  	  || (GET_CODE (x) == REG && true_regnum (x) == -1)
  	  || (GET_CODE (x) == SUBREG
  	      && (GET_CODE (SUBREG_REG (x)) == MEM
  		  || (GET_CODE (SUBREG_REG (x)) == REG
  		      && true_regnum (SUBREG_REG (x)) == -1))))
  	return GENERAL_REGS;
      }
  
    return NO_REGS;
  }
  
  /* Handle FUNCTION_VALUE, FUNCTION_OUTGOING_VALUE, and LIBCALL_VALUE
     macros.  */
  rtx
  mt_function_value (tree valtype, enum machine_mode mode, tree func_decl ATTRIBUTE_UNUSED)
  {
    if ((mode) == DImode || (mode) == DFmode)
      return gen_rtx_MEM (mode, gen_rtx_REG (mode, RETURN_VALUE_REGNUM));
  
    if (valtype)
      mode = TYPE_MODE (valtype);
  
    return gen_rtx_REG (mode, RETURN_VALUE_REGNUM);
  }
  
  /* Split a move into two smaller pieces.
     MODE indicates the reduced mode.  OPERANDS[0] is the original destination
     OPERANDS[1] is the original src.  The new destinations are
     OPERANDS[2] and OPERANDS[4], while the new sources are OPERANDS[3]
     and OPERANDS[5].  */
  
  void
  mt_split_words (enum machine_mode nmode,
  		 enum machine_mode omode,
  		 rtx *operands)
  {
    rtx dl,dh;	/* src/dest pieces.  */
    rtx sl,sh;
    int	move_high_first = 0;	/* Assume no overlap.  */
  
    switch (GET_CODE (operands[0])) /* Dest.  */
      {
      case SUBREG:
      case REG:
        if ((GET_CODE (operands[1]) == REG
  	   || GET_CODE (operands[1]) == SUBREG)
  	  && true_regnum (operands[0]) <= true_regnum (operands[1]))
  	move_high_first = 1;
  
        if (GET_CODE (operands[0]) == SUBREG)
  	{
  	  dl = gen_rtx_SUBREG (nmode, SUBREG_REG (operands[0]),
  			       SUBREG_BYTE (operands[0]) + GET_MODE_SIZE (nmode));
  	  dh = gen_rtx_SUBREG (nmode, SUBREG_REG (operands[0]), SUBREG_BYTE (operands[0]));
  	}
        else if (GET_CODE (operands[0]) == REG && ! IS_PSEUDO_P (operands[0]))
  	{
  	  int	r = REGNO (operands[0]);
  	  dh = gen_rtx_REG (nmode, r);
  	  dl = gen_rtx_REG (nmode, r + HARD_REGNO_NREGS (r, nmode));
  	}
        else
  	{
  	  dh = gen_rtx_SUBREG (nmode, operands[0], 0);
  	  dl = gen_rtx_SUBREG (nmode, operands[0], GET_MODE_SIZE (nmode));
  	}
        break;
  
      case MEM:
        switch (GET_CODE (XEXP (operands[0], 0)))
  	{
  	case POST_INC:
  	case POST_DEC:
  	  gcc_unreachable ();
  	default:
  	  dl = operand_subword (operands[0],
  				GET_MODE_SIZE (nmode)/UNITS_PER_WORD,
  				0, omode);
  	  dh = operand_subword (operands[0], 0, 0, omode);
  	}
        break;
      default:
        gcc_unreachable ();
      }
  
    switch (GET_CODE (operands[1]))
      {
      case REG:
        if (! IS_PSEUDO_P (operands[1]))
  	{
  	  int r = REGNO (operands[1]);
  
  	  sh = gen_rtx_REG (nmode, r);
  	  sl = gen_rtx_REG (nmode, r + HARD_REGNO_NREGS (r, nmode));
  	}
        else
  	{
  	  sh = gen_rtx_SUBREG (nmode, operands[1], 0);
  	  sl = gen_rtx_SUBREG (nmode, operands[1], GET_MODE_SIZE (nmode));
  	}
        break;
  
      case CONST_DOUBLE:
        if (operands[1] == const0_rtx)
  	sh = sl = const0_rtx;
        else
  	split_double (operands[1], & sh, & sl);
        break;
  
      case CONST_INT:
        if (operands[1] == const0_rtx)
  	sh = sl = const0_rtx;
        else
  	{
  	  int vl, vh;
  
  	  switch (nmode)
  	    {
  	    default:
  	      gcc_unreachable ();
  	    }
  	    
  	  sl = GEN_INT (vl);
  	  sh = GEN_INT (vh);
  	}
        break;
  
      case SUBREG:
        sl = gen_rtx_SUBREG (nmode,
  			   SUBREG_REG (operands[1]),
  			   SUBREG_BYTE (operands[1]) + GET_MODE_SIZE (nmode));
        sh = gen_rtx_SUBREG (nmode,
  			   SUBREG_REG (operands[1]),
  			   SUBREG_BYTE (operands[1]));
        break;
  
      case MEM:
        switch (GET_CODE (XEXP (operands[1], 0)))
  	{
  	case POST_DEC:
  	case POST_INC:
  	  gcc_unreachable ();
  	  break;
  	default:
  	  sl = operand_subword (operands[1], 
  				GET_MODE_SIZE (nmode)/UNITS_PER_WORD,
  				0, omode);
  	  sh = operand_subword (operands[1], 0, 0, omode);
  	  
  	  /* Check if the DF load is going to clobber the register
               used for the address, and if so make sure that is going
               to be the second move.  */
  	  if (GET_CODE (dl) == REG
  	      && true_regnum (dl)
  	      == true_regnum (XEXP (XEXP (sl, 0 ), 0)))
  	    move_high_first = 1;
  	}
        break;
      default:
        gcc_unreachable ();
      }
  
    if (move_high_first)
      {
        operands[2] = dh;
        operands[3] = sh;
        operands[4] = dl;
        operands[5] = sl;
      }
    else
      {
        operands[2] = dl;
        operands[3] = sl;
        operands[4] = dh;
        operands[5] = sh;
      }
    return;
  }
  
  /* Implement TARGET_MUST_PASS_IN_STACK hook.  */
  static bool
  mt_pass_in_stack (enum machine_mode mode ATTRIBUTE_UNUSED, tree type)
  {
    return (((type) != 0
  	   && (TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST
  	       || TREE_ADDRESSABLE (type))));
  }
  
  /* Increment the counter for the number of loop instructions in the
     current function.  */
  
  void mt_add_loop (void)
  {
    cfun->machine->has_loops++;
  }
  
  
  /* Maximum loop nesting depth.  */
  #define MAX_LOOP_DEPTH 4
  /* Maximum size of a loop (allows some headroom for delayed branch slot
     filling.  */
  #define MAX_LOOP_LENGTH (200 * 4)
  
  /* We need to keep a vector of loops */
  typedef struct loop_info *loop_info;
  DEF_VEC_P (loop_info);
  DEF_VEC_ALLOC_P (loop_info,heap);
  
  /* Information about a loop we have found (or are in the process of
     finding).  */
  struct loop_info GTY (())
  {
    /* loop number, for dumps */
    int loop_no;
    
    /* Predecessor block of the loop.   This is the one that falls into
       the loop and contains the initialization instruction.  */
    basic_block predecessor;
  
    /* First block in the loop.  This is the one branched to by the dbnz
       insn.  */
    basic_block head;
    
    /* Last block in the loop (the one with the dbnz insn */
    basic_block tail;
  
    /* The successor block of the loop.  This is the one the dbnz insn
       falls into.  */
    basic_block successor;
  
    /* The dbnz insn.  */
    rtx dbnz;
  
    /* The initialization insn.  */
    rtx init;
  
    /* The new initialization instruction.  */
    rtx loop_init;
  
    /* The new ending instruction. */
    rtx loop_end;
  
    /* The new label placed at the end of the loop. */
    rtx end_label;
  
    /* The nesting depth of the loop.  Set to -1 for a bad loop.  */
    int depth;
  
    /* The length of the loop.  */
    int length;
  
    /* Next loop in the graph. */
    struct loop_info *next;
  
    /* Vector of blocks only within the loop, (excluding those within
       inner loops).  */
    VEC (basic_block,heap) *blocks;
  
    /* Vector of inner loops within this loop  */
    VEC (loop_info,heap) *loops;
  };
  
  /* Information used during loop detection.  */
  typedef struct loop_work GTY(())
  {
    /* Basic block to be scanned.  */
    basic_block block;
  
    /* Loop it will be within.  */
    loop_info loop;
  } loop_work;
  
  /* Work list.  */
  DEF_VEC_O (loop_work);
  DEF_VEC_ALLOC_O (loop_work,heap);
  
  /* Determine the nesting and length of LOOP.  Return false if the loop
     is bad.  */
  
  static bool
  mt_loop_nesting (loop_info loop)
  {
    loop_info inner;
    unsigned ix;
    int inner_depth = 0;
    
    if (!loop->depth)
      {
        /* Make sure we only have one entry point.  */
        if (EDGE_COUNT (loop->head->preds) == 2)
  	{
  	  loop->predecessor = EDGE_PRED (loop->head, 0)->src;
  	  if (loop->predecessor == loop->tail)
  	    /* We wanted the other predecessor.  */
  	    loop->predecessor = EDGE_PRED (loop->head, 1)->src;
  	  
  	  /* We can only place a loop insn on a fall through edge of a
  	     single exit block.  */
  	  if (EDGE_COUNT (loop->predecessor->succs) != 1
  	      || !(EDGE_SUCC (loop->predecessor, 0)->flags & EDGE_FALLTHRU))
  	    loop->predecessor = NULL;
  	}
  
        /* Mark this loop as bad for now.  */
        loop->depth = -1;
        if (loop->predecessor)
  	{
  	  for (ix = 0; VEC_iterate (loop_info, loop->loops, ix++, inner);)
  	    {
  	      if (!inner->depth)
  		mt_loop_nesting (inner);
  	      
  	      if (inner->depth < 0)
  		{
  		  inner_depth = -1;
  		  break;
  		}
  	      
  	      if (inner_depth < inner->depth)
  		inner_depth = inner->depth;
  	      loop->length += inner->length;
  	    }
  	  
  	  /* Set the proper loop depth, if it was good. */
  	  if (inner_depth >= 0)
  	    loop->depth = inner_depth + 1;
  	}
      }
    return (loop->depth > 0
  	  && loop->predecessor
  	  && loop->depth < MAX_LOOP_DEPTH
  	  && loop->length < MAX_LOOP_LENGTH);
  }
  
  /* Determine the length of block BB.  */
  
  static int
  mt_block_length (basic_block bb)
  {
    int length = 0;
    rtx insn;
  
    for (insn = BB_HEAD (bb);
         insn != NEXT_INSN (BB_END (bb));
         insn = NEXT_INSN (insn))
      {
        if (!INSN_P (insn))
  	continue;
        if (CALL_P (insn))
  	{
  	  /* Calls are not allowed in loops.  */
  	  length = MAX_LOOP_LENGTH + 1;
  	  break;
  	}
        
        length += get_attr_length (insn);
      }
    return length;
  }
  
  /* Scan the blocks of LOOP (and its inferiors) looking for uses of
     REG.  Return true, if we find any.  Don't count the loop's dbnz
     insn if it matches DBNZ.  */
  
  static bool
  mt_scan_loop (loop_info loop, rtx reg, rtx dbnz)
  {
    unsigned ix;
    loop_info inner;
    basic_block bb;
    
    for (ix = 0; VEC_iterate (basic_block, loop->blocks, ix, bb); ix++)
      {
        rtx insn;
  
        for (insn = BB_HEAD (bb);
  	   insn != NEXT_INSN (BB_END (bb));
  	   insn = NEXT_INSN (insn))
  	{
  	  if (!INSN_P (insn))
  	    continue;
  	  if (insn == dbnz)
  	    continue;
  	  if (reg_mentioned_p (reg, PATTERN (insn)))
  	    return true;
  	}
      }
    for (ix = 0; VEC_iterate (loop_info, loop->loops, ix, inner); ix++)
      if (mt_scan_loop (inner, reg, NULL_RTX))
        return true;
    
    return false;
  }
  
  /* MS2 has a loop instruction which needs to be placed just before the
     loop.  It indicates the end of the loop and specifies the number of
     loop iterations.  It can be nested with an automatically maintained
     stack of counter and end address registers.  It's an ideal
     candidate for doloop.  Unfortunately, gcc presumes that loops
     always end with an explicit instruction, and the doloop_begin
     instruction is not a flow control instruction so it can be
     scheduled earlier than just before the start of the loop.  To make
     matters worse, the optimization pipeline can duplicate loop exit
     and entrance blocks and fails to track abnormally exiting loops.
     Thus we cannot simply use doloop.
  
     What we do is emit a dbnz pattern for the doloop optimization, and
     let that be optimized as normal.  Then in machine dependent reorg
     we have to repeat the loop searching algorithm.  We use the
     flow graph to find closed loops ending in a dbnz insn.  We then try
     and convert it to use the loop instruction.  The conditions are,
  
     * the loop has no abnormal exits, duplicated end conditions or
     duplicated entrance blocks
  
     * the loop counter register is only used in the dbnz instruction
     within the loop
     
     * we can find the instruction setting the initial value of the loop
     counter
  
     * the loop is not executed more than 65535 times. (This might be
     changed to 2^32-1, and would therefore allow variable initializers.)
  
     * the loop is not nested more than 4 deep 5) there are no
     subroutine calls in the loop.  */
  
  static void
  mt_reorg_loops (FILE *dump_file)
  {
    basic_block bb;
    loop_info loops = NULL;
    loop_info loop;
    int nloops = 0;
    unsigned dwork = 0;
    VEC (loop_work,heap) *works = VEC_alloc (loop_work,heap,20);
    loop_work *work;
    edge e;
    edge_iterator ei;
    bool replaced = false;
  
    /* Find all the possible loop tails.  This means searching for every
       dbnz instruction.  For each one found, create a loop_info
       structure and add the head block to the work list. */
    FOR_EACH_BB (bb)
      {
        rtx tail = BB_END (bb);
  
        while (GET_CODE (tail) == NOTE)
  	tail = PREV_INSN (tail);
        
        bb->aux = NULL;
        if (recog_memoized (tail) == CODE_FOR_decrement_and_branch_until_zero)
  	{
  	  /* A possible loop end */
  
  	  loop = XNEW (struct loop_info);
  	  loop->next = loops;
  	  loops = loop;
  	  loop->tail = bb;
  	  loop->head = BRANCH_EDGE (bb)->dest;
  	  loop->successor = FALLTHRU_EDGE (bb)->dest;
  	  loop->predecessor = NULL;
  	  loop->dbnz = tail;
  	  loop->depth = 0;
  	  loop->length = mt_block_length (bb);
  	  loop->blocks = VEC_alloc (basic_block, heap, 20);
  	  VEC_quick_push (basic_block, loop->blocks, bb);
  	  loop->loops = NULL;
  	  loop->loop_no = nloops++;
  	  
  	  loop->init = loop->end_label = NULL_RTX;
  	  loop->loop_init = loop->loop_end = NULL_RTX;
  	  
  	  work = VEC_safe_push (loop_work, heap, works, NULL);
  	  work->block = loop->head;
  	  work->loop = loop;
  
  	  bb->aux = loop;
  
  	  if (dump_file)
  	    {
  	      fprintf (dump_file, ";; potential loop %d ending at\n",
  		       loop->loop_no);
  	      print_rtl_single (dump_file, tail);
  	    }
  	}
      }
  
    /*  Now find all the closed loops.
        until work list empty,
         if block's auxptr is set
           if != loop slot
             if block's loop's start != block
  	     mark loop as bad
  	   else
               append block's loop's fallthrough block to worklist
  	     increment this loop's depth
         else if block is exit block
           mark loop as bad
         else
       	  set auxptr
  	  for each target of block
       	    add to worklist */
    while (VEC_iterate (loop_work, works, dwork++, work))
      {
        loop = work->loop;
        bb = work->block;
        if (bb == EXIT_BLOCK_PTR)
  	/* We've reached the exit block.  The loop must be bad. */
  	loop->depth = -1;
        else if (!bb->aux)
  	{
  	  /* We've not seen this block before.  Add it to the loop's
  	     list and then add each successor to the work list.  */
  	  bb->aux = loop;
  	  loop->length += mt_block_length (bb);
  	  VEC_safe_push (basic_block, heap, loop->blocks, bb);
  	  FOR_EACH_EDGE (e, ei, bb->succs)
  	    {
  	      if (!VEC_space (loop_work, works, 1))
  		{
  		  if (dwork)
  		    {
  		      VEC_block_remove (loop_work, works, 0, dwork);
  		      dwork = 0;
  		    }
  		  else
  		    VEC_reserve (loop_work, heap, works, 1);
  		}
  	      work = VEC_quick_push (loop_work, works, NULL);
  	      work->block = EDGE_SUCC (bb, ei.index)->dest;
  	      work->loop = loop;
  	    }
  	}
        else if (bb->aux != loop)
  	{
  	  /* We've seen this block in a different loop.  If it's not
  	     the other loop's head, then this loop must be bad.
  	     Otherwise, the other loop might be a nested loop, so
  	     continue from that loop's successor.  */
  	  loop_info other = bb->aux;
  	  
  	  if (other->head != bb)
  	    loop->depth = -1;
  	  else
  	    {
  	      VEC_safe_push (loop_info, heap, loop->loops, other);
  	      work = VEC_safe_push (loop_work, heap, works, NULL);
  	      work->loop = loop;
  	      work->block = other->successor;
  	    }
  	}
      }
    VEC_free (loop_work, heap, works);
  
    /* Now optimize the loops.  */
    for (loop = loops; loop; loop = loop->next)
      {
        rtx iter_reg, insn, init_insn;
        rtx init_val, loop_end, loop_init, end_label, head_label;
  
        if (!mt_loop_nesting (loop))
  	{
  	  if (dump_file)
  	    fprintf (dump_file, ";; loop %d is bad\n", loop->loop_no);
  	  continue;
  	}
  
        /* Get the loop iteration register.  */
        iter_reg = SET_DEST (XVECEXP (PATTERN (loop->dbnz), 0, 1));
        
        if (!REG_P (iter_reg))
  	{
  	  /* Spilled */
  	  if (dump_file)
  	    fprintf (dump_file, ";; loop %d has spilled iteration count\n",
  		     loop->loop_no);
  	  continue;
  	}
  
        /* Look for the initializing insn */
        init_insn = NULL_RTX;
        for (insn = BB_END (loop->predecessor);
  	   insn != PREV_INSN (BB_HEAD (loop->predecessor));
  	   insn = PREV_INSN (insn))
  	{
  	  if (!INSN_P (insn))
  	    continue;
  	  if (reg_mentioned_p (iter_reg, PATTERN (insn)))
  	    {
  	      rtx set = single_set (insn);
  
  	      if (set && rtx_equal_p (iter_reg, SET_DEST (set)))
  		init_insn = insn;
  	      break;
  	    }
  	}
  
        if (!init_insn)
  	{
  	  if (dump_file)
  	    fprintf (dump_file, ";; loop %d has no initializer\n",
  		     loop->loop_no);
  	  continue;
  	}
        if (dump_file)
  	{
  	  fprintf (dump_file, ";; loop %d initialized by\n",
  		   loop->loop_no);
  	  print_rtl_single (dump_file, init_insn);
  	}
  
        init_val = PATTERN (init_insn);
        if (GET_CODE (init_val) == SET)
  	init_val = SET_SRC (init_val);
        if (GET_CODE (init_val) != CONST_INT || INTVAL (init_val) >= 65535)
  	{
  	  if (dump_file)
  	    fprintf (dump_file, ";; loop %d has complex initializer\n",
  		     loop->loop_no);
  	  continue;
  	}
        
        /* Scan all the blocks to make sure they don't use iter_reg.  */
        if (mt_scan_loop (loop, iter_reg, loop->dbnz))
  	{
  	  if (dump_file)
  	    fprintf (dump_file, ";; loop %d uses iterator\n",
  		     loop->loop_no);
  	  continue;
  	}
  
        /* The loop is good for replacement.  */
        
        /* loop is 1 based, dbnz is zero based.  */
        init_val = GEN_INT (INTVAL (init_val) + 1);
        
        iter_reg = gen_rtx_REG (SImode, LOOP_FIRST + loop->depth - 1);
        end_label = gen_label_rtx ();
        head_label = XEXP (SET_SRC (XVECEXP (PATTERN (loop->dbnz), 0, 0)), 1);
        loop_end = gen_loop_end (iter_reg, head_label);
        loop_init = gen_loop_init (iter_reg, init_val, end_label);
        loop->init = init_insn;
        loop->end_label = end_label;
        loop->loop_init = loop_init;
        loop->loop_end = loop_end;
        replaced = true;
        
        if (dump_file)
  	{
  	  fprintf (dump_file, ";; replacing loop %d initializer with\n",
  		   loop->loop_no);
  	  print_rtl_single (dump_file, loop->loop_init);
  	  fprintf (dump_file, ";; replacing loop %d terminator with\n",
  		   loop->loop_no);
  	  print_rtl_single (dump_file, loop->loop_end);
  	}
      }
  
    /* Now apply the optimizations.  Do it this way so we don't mess up
       the flow graph half way through.  */
    for (loop = loops; loop; loop = loop->next)
      if (loop->loop_init)
        {
  	emit_jump_insn_after (loop->loop_init, BB_END (loop->predecessor));
  	delete_insn (loop->init);
  	emit_label_before (loop->end_label, loop->dbnz);
  	emit_jump_insn_before (loop->loop_end, loop->dbnz);
  	delete_insn (loop->dbnz);
        }
  
    /* Free up the loop structures */
    while (loops)
      {
        loop = loops;
        loops = loop->next;
        VEC_free (loop_info, heap, loop->loops);
        VEC_free (basic_block, heap, loop->blocks);
        XDELETE (loop);
      }
  
    if (replaced && dump_file)
      {
        fprintf (dump_file, ";; Replaced loops\n");
        print_rtl (dump_file, get_insns ());
      }
  }
  
  /* Structures to hold branch information during reorg.  */
  typedef struct branch_info
  {
    rtx insn;  /* The branch insn.  */
    
    struct branch_info *next;
  } branch_info;
  
  typedef struct label_info
  {
    rtx label;  /* The label.  */
    branch_info *branches;  /* branches to this label.  */
    struct label_info *next;
  } label_info;
  
  /* Chain of labels found in current function, used during reorg.  */
  static label_info *mt_labels;
  
  /* If *X is a label, add INSN to the list of branches for that
     label.  */
  
  static int
  mt_add_branches (rtx *x, void *insn)
  {
    if (GET_CODE (*x) == LABEL_REF)
      {
        branch_info *branch = xmalloc (sizeof (*branch));
        rtx label = XEXP (*x, 0);
        label_info *info;
  
        for (info = mt_labels; info; info = info->next)
  	if (info->label == label)
  	  break;
  
        if (!info)
  	{
  	  info = xmalloc (sizeof (*info));
  	  info->next = mt_labels;
  	  mt_labels = info;
  	  
  	  info->label = label;
  	  info->branches = NULL;
  	}
  
        branch->next = info->branches;
        info->branches = branch;
        branch->insn = insn;
      }
    return 0;
  }
  
  /* If BRANCH has a filled delay slot, check if INSN is dependent upon
     it.  If so, undo the delay slot fill.   Returns the next insn, if
     we patch out the branch.  Returns the branch insn, if we cannot
     patch out the branch (due to anti-dependency in the delay slot).
     In that case, the caller must insert nops at the branch target.  */
  
  static rtx
  mt_check_delay_slot (rtx branch, rtx insn)
  {
    rtx slot;
    rtx tmp;
    rtx p;
    rtx jmp;
    
    gcc_assert (GET_CODE (PATTERN (branch)) == SEQUENCE);
    if (INSN_DELETED_P (branch))
      return NULL_RTX;
    slot = XVECEXP (PATTERN (branch), 0, 1);
    
    tmp = PATTERN (insn);
    note_stores (PATTERN (slot), insn_dependent_p_1, &tmp);
    if (tmp)
      /* Not dependent.  */
      return NULL_RTX;
    
    /* Undo the delay slot.  */
    jmp = XVECEXP (PATTERN (branch), 0, 0);
    
    tmp = PATTERN (jmp);
    note_stores (PATTERN (slot), insn_dependent_p_1, &tmp);
    if (!tmp)
      /* Anti dependent. */
      return branch;
        
    p = PREV_INSN (branch);
    NEXT_INSN (p) = slot;
    PREV_INSN (slot) = p;
    NEXT_INSN (slot) = jmp;
    PREV_INSN (jmp) = slot;
    NEXT_INSN (jmp) = branch;
    PREV_INSN (branch) = jmp;
    XVECEXP (PATTERN (branch), 0, 0) = NULL_RTX;
    XVECEXP (PATTERN (branch), 0, 1) = NULL_RTX;
    delete_insn (branch);
    return jmp;
  }
  
  /* Insert nops to satisfy pipeline constraints.  We only deal with ms2
     constraints here.  Earlier CPUs are dealt with by inserting nops with
     final_prescan (but that can lead to inferior code, and is
     impractical with ms2's JAL hazard).
  
     ms2 dynamic constraints
     1) a load and a following use must be separated by one insn
     2) an insn and a following dependent call must be separated by two insns
     
     only arith insns are placed in delay slots so #1 cannot happen with
     a load in a delay slot.  #2 can happen with an arith insn in the
     delay slot.  */
  
  static void
  mt_reorg_hazard (void)
  {
    rtx insn, next;
  
    /* Find all the branches */
    for (insn = get_insns ();
         insn;
         insn = NEXT_INSN (insn))
      {
        rtx jmp;
  
        if (!INSN_P (insn))
  	continue;
  
        jmp = PATTERN (insn);
        
        if (GET_CODE (jmp) != SEQUENCE)
  	/* If it's not got a filled delay slot, then it can't
  	   conflict.  */
  	continue;
        
        jmp = XVECEXP (jmp, 0, 0);
  
        if (recog_memoized (jmp) == CODE_FOR_tablejump)
  	for (jmp = XEXP (XEXP (XVECEXP (PATTERN (jmp), 0, 1), 0), 0);
  	     !JUMP_TABLE_DATA_P (jmp);
  	     jmp = NEXT_INSN (jmp))
  	  continue;
  
        for_each_rtx (&PATTERN (jmp), mt_add_branches, insn);
      }
  
    /* Now scan for dependencies.  */
    for (insn = get_insns ();
         insn && !INSN_P (insn);
         insn = NEXT_INSN (insn))
      continue;
    
    for (;
         insn;
         insn = next)
      {
        rtx jmp, tmp;
        enum attr_type attr;
        
        gcc_assert (INSN_P (insn) && !INSN_DELETED_P (insn));
        for (next = NEXT_INSN (insn);
  	   next;
  	   next = NEXT_INSN (next))
  	{
  	  if (!INSN_P (next))
  	    continue;
  	  if (GET_CODE (PATTERN (next)) != USE)
  	    break;
  	}
  
        jmp = insn;
        if (GET_CODE (PATTERN (insn)) == SEQUENCE)
  	jmp = XVECEXP (PATTERN (insn), 0, 0);
        
        attr = recog_memoized (jmp) >= 0 ? get_attr_type (jmp) : TYPE_UNKNOWN;
        
        if (next && attr == TYPE_LOAD)
  	{
  	  /* A load.  See if NEXT is dependent, and if so insert a
  	     nop.  */
  	  
  	  tmp = PATTERN (next);
  	  if (GET_CODE (tmp) == SEQUENCE)
  	    tmp = PATTERN (XVECEXP (tmp, 0, 0));
  	  note_stores (PATTERN (insn), insn_dependent_p_1, &tmp);
  	  if (!tmp)
  	    emit_insn_after (gen_nop (), insn);
  	}
        
        if (attr == TYPE_CALL)
  	{
  	  /* A call.  Make sure we're not dependent on either of the
  	     previous two dynamic instructions.  */
  	  int nops = 0;
  	  int count;
  	  rtx prev = insn;
  	  rtx rescan = NULL_RTX;
  
  	  for (count = 2; count && !nops;)
  	    {
  	      int type;
  	      
  	      prev = PREV_INSN (prev);
  	      if (!prev)
  		{
  		  /* If we reach the start of the function, we must
  		     presume the caller set the address in the delay
  		     slot of the call instruction.  */
  		  nops = count;
  		  break;
  		}
  	      
  	      if (BARRIER_P (prev))
  		break;
  	      if (LABEL_P (prev))
  		{
  		  /* Look at branches to this label.  */
  		  label_info *label;
  		  branch_info *branch;
  
  		  for (label = mt_labels;
  		       label;
  		       label = label->next)
  		    if (label->label == prev)
  		      {
  			for (branch = label->branches;
  			     branch;
  			     branch = branch->next)
  			  {
  			    tmp = mt_check_delay_slot (branch->insn, jmp);
  
  			    if (tmp == branch->insn)
  			      {
  				nops = count;
  				break;
  			      }
  			    
  			    if (tmp && branch->insn == next)
  			      rescan = tmp;
  			  }
  			break;
  		      }
  		  continue;
  		}
  	      if (!INSN_P (prev) || GET_CODE (PATTERN (prev)) == USE)
  		continue;
  	      
  	      if (GET_CODE (PATTERN (prev)) == SEQUENCE)
  		{
  		  /* Look at the delay slot.  */
  		  tmp = mt_check_delay_slot (prev, jmp);
  		  if (tmp == prev)
  		    nops = count;
  		  break;
  		}
  	      
  	      type = (INSN_CODE (prev) >= 0 ? get_attr_type (prev)
  		      : TYPE_COMPLEX);
  	      if (type == TYPE_CALL || type == TYPE_BRANCH)
  		break;
  	      
  	      if (type == TYPE_LOAD
  		  || type == TYPE_ARITH
  		  || type == TYPE_COMPLEX)
  		{
  		  tmp = PATTERN (jmp);
  		  note_stores (PATTERN (prev), insn_dependent_p_1, &tmp);
  		  if (!tmp)
  		    {
  		      nops = count;
  		      break;
  		    }
  		}
  
  	      if (INSN_CODE (prev) >= 0)
  		count--;
  	    }
  
  	  if (rescan)
  	    for (next = NEXT_INSN (rescan);
  		 next && !INSN_P (next);
  		 next = NEXT_INSN (next))
  	      continue;
  	  while (nops--)
  	    emit_insn_before (gen_nop (), insn);
  	}
      }
  
    /* Free the data structures.  */
    while (mt_labels)
      {
        label_info *label = mt_labels;
        branch_info *branch, *next;
        
        mt_labels = label->next;
        for (branch = label->branches; branch; branch = next)
  	{
  	  next = branch->next;
  	  free (branch);
  	}
        free (label);
      }
  }
  
  /* Fixup the looping instructions, do delayed branch scheduling, fixup
     scheduling hazards.  */
  
  static void
  mt_machine_reorg (void)
  {
    if (cfun->machine->has_loops && TARGET_MS2)
      mt_reorg_loops (dump_file);
  
    if (mt_flag_delayed_branch)
      dbr_schedule (get_insns ());
    
    if (TARGET_MS2)
      {
        /* Force all instructions to be split into their final form.  */
        split_all_insns_noflow ();
        mt_reorg_hazard ();
      }
  }
  
  /* Initialize the GCC target structure.  */
  const struct attribute_spec mt_attribute_table[];
  
  #undef  TARGET_ATTRIBUTE_TABLE
  #define TARGET_ATTRIBUTE_TABLE 		mt_attribute_table
  #undef  TARGET_STRUCT_VALUE_RTX
  #define TARGET_STRUCT_VALUE_RTX		mt_struct_value_rtx
  #undef  TARGET_PROMOTE_PROTOTYPES
  #define TARGET_PROMOTE_PROTOTYPES	hook_bool_tree_true
  #undef  TARGET_PASS_BY_REFERENCE
  #define TARGET_PASS_BY_REFERENCE	mt_pass_by_reference
  #undef  TARGET_MUST_PASS_IN_STACK
  #define TARGET_MUST_PASS_IN_STACK       mt_pass_in_stack
  #undef  TARGET_ARG_PARTIAL_BYTES
  #define TARGET_ARG_PARTIAL_BYTES	mt_arg_partial_bytes
  #undef  TARGET_SETUP_INCOMING_VARARGS
  #define TARGET_SETUP_INCOMING_VARARGS 	mt_setup_incoming_varargs
  #undef  TARGET_MACHINE_DEPENDENT_REORG
  #define TARGET_MACHINE_DEPENDENT_REORG  mt_machine_reorg
  
  struct gcc_target targetm = TARGET_INITIALIZER;
  
  #include "gt-mt.h"
  