;; GCC machine description for Matsushita MN10300
;; Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
;; 2007, 2008 Free Software Foundation, Inc.
;; Contributed by Jeff Law (law@cygnus.com).

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

;; The original PO technology requires these to be ordered by speed,
;; so that assigner will pick the fastest.

;; See file "rtl.def" for documentation on define_insn, match_*, et. al.

;; Condition code settings.
;; none - insn does not affect cc
;; none_0hit - insn does not affect cc but it does modify operand 0
;;	This attribute is used to keep track of when operand 0 changes.
;; 	See the description of NOTICE_UPDATE_CC for more info.
;; set_znv - insn sets z,n,v to usable values; c is unusable.
;; set_zn  - insn sets z,n to usable values; v,c are unusable.
;; compare - compare instruction
;; clobber - value of cc is unknown
(define_attr "cc" "none,none_0hit,set_znv,set_zn,compare,clobber"
  (const_string "clobber"))

(define_constants [
  (PIC_REG	6)
  (SP_REG	9)

  (UNSPEC_INT_LABEL	0)
  (UNSPEC_PIC		1)
  (UNSPEC_GOT		2)
  (UNSPEC_GOTOFF	3)
  (UNSPEC_PLT		4)
  (UNSPEC_GOTSYM_OFF	5)
])

(include "predicates.md")
(include "constraints.md")

;; ----------------------------------------------------------------------
;; MOVE INSTRUCTIONS
;; ----------------------------------------------------------------------

;; movqi

(define_expand "movqi"
  [(set (match_operand:QI 0 "general_operand" "")
	(match_operand:QI 1 "general_operand" ""))]
  ""
  "
{
  /* One of the ops has to be in a register */
  if (!register_operand (operand0, QImode)
      && !register_operand (operand1, QImode))
    operands[1] = copy_to_mode_reg (QImode, operand1);
}")

(define_insn ""
  [(set (match_operand:QI 0 "nonimmediate_operand" "=d*x*a*f,d*x,d*x*a,d*x*a,m,*f,d*x*a")
	(match_operand:QI 1 "general_operand" "0,I,d*xai,m,d*xa,d*xa*f,*f"))]
  "TARGET_AM33
   && (register_operand (operands[0], QImode)
       || register_operand (operands[1], QImode))"
  "*
{
  switch (which_alternative)
    {
    case 0:
      return \"nop\";
    case 1:
      return \"clr %0\";
    case 2:
      if (GET_CODE (operands[1]) == CONST_DOUBLE)
	{
	  rtx xoperands[2];
	  xoperands[0] = operands[0];
	  xoperands[1] = GEN_INT (CONST_DOUBLE_LOW (operands[1]));
	  output_asm_insn (\"mov %1,%0\", xoperands);
	  return \"\";
	}

      if (REGNO_REG_CLASS (true_regnum (operands[0])) == EXTENDED_REGS
	  && GET_CODE (operands[1]) == CONST_INT)
	{
	  HOST_WIDE_INT val = INTVAL (operands[1]);

	  if (((val & 0x80) && ! (val & 0xffffff00))
	      || ((val & 0x800000) && ! (val & 0xff000000)))
	    return \"movu %1,%0\";
	}
      return \"mov %1,%0\";
    case 3:
    case 4:
      return \"movbu %1,%0\";
    case 5:
    case 6:
      return \"fmov %1,%0\";
    default:
      gcc_unreachable ();
    }
}"
  [(set_attr "cc" "none,clobber,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit")])

(define_insn ""
  [(set (match_operand:QI 0 "nonimmediate_operand" "=d*a,d,d,!*a,d*a,d,m")
	(match_operand:QI 1 "general_operand" "0,I,i,i,da,m,d"))]
  "register_operand (operands[0], QImode)
   || register_operand (operands[1], QImode)"
  "*
{
  switch (which_alternative)
    {
    case 0:
      return \"nop\";
    case 1:
      return \"clr %0\";
    case 2:
    case 3:
    case 4:
      if (GET_CODE (operands[1]) == CONST_DOUBLE)
	{
	  rtx xoperands[2];
	  xoperands[0] = operands[0];
	  xoperands[1] = GEN_INT (CONST_DOUBLE_LOW (operands[1]));
	  output_asm_insn (\"mov %1,%0\", xoperands);
	  return \"\";
	}

      return \"mov %1,%0\";
    case 5:
    case 6:
      return \"movbu %1,%0\";
    default:
      gcc_unreachable ();
    }
}"
  [(set_attr "cc" "none,clobber,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit")])

;; movhi

(define_expand "movhi"
  [(set (match_operand:HI 0 "general_operand" "")
	(match_operand:HI 1 "general_operand" ""))]
  ""
  "
{
  /* One of the ops has to be in a register */
  if (!register_operand (operand1, HImode)
      && !register_operand (operand0, HImode))
    operands[1] = copy_to_mode_reg (HImode, operand1);
}")

(define_insn ""
  [(set (match_operand:HI 0 "nonimmediate_operand" "=d*x*a*f,d*x,d*x*a,d*x*a,m,*f,d*x*a")
	(match_operand:HI 1 "general_operand" "0,I,d*x*ai,m,d*x*a,d*x*a*f,*f"))]
  "TARGET_AM33
   && (register_operand (operands[0], HImode)
       || register_operand (operands[1], HImode))"
  "*
{
  switch (which_alternative)
    {
    case 0:
      return \"nop\";
    case 1:
      return \"clr %0\";
    case 2:
      if (GET_CODE (operands[1]) == CONST_DOUBLE)
	{
	  rtx xoperands[2];
	  xoperands[0] = operands[0];
	  xoperands[1] = GEN_INT (CONST_DOUBLE_LOW (operands[1]));
	  output_asm_insn (\"mov %1,%0\", xoperands);
	  return \"\";
	}

      if (REGNO_REG_CLASS (true_regnum (operands[0])) == EXTENDED_REGS
	  && GET_CODE (operands[1]) == CONST_INT)
	{
	  HOST_WIDE_INT val = INTVAL (operands[1]);

	  if (((val & 0x80) && ! (val & 0xffffff00))
	      || ((val & 0x800000) && ! (val & 0xff000000)))
	    return \"movu %1,%0\";
	}
      return \"mov %1,%0\";
    case 3:
    case 4:
      return \"movhu %1,%0\";
    case 5:
    case 6:
      return \"fmov %1,%0\";
    default:
      gcc_unreachable ();
    }
}"
  [(set_attr "cc" "none,clobber,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit")])

(define_insn ""
  [(set (match_operand:HI 0 "nonimmediate_operand" "=d*a,d,d,!*a,d*a,d,m")
	(match_operand:HI 1 "general_operand" "0,I,i,i,da,m,d"))]
  "register_operand (operands[0], HImode)
   || register_operand (operands[1], HImode)"
  "*
{
  switch (which_alternative)
    {
    case 0:
      return \"nop\";
    case 1:
      return \"clr %0\";
    case 2:
    case 3:
    case 4:
      if (GET_CODE (operands[1]) == CONST_DOUBLE)
	{
	  rtx xoperands[2];
	  xoperands[0] = operands[0];
	  xoperands[1] = GEN_INT (CONST_DOUBLE_LOW (operands[1]));
	  output_asm_insn (\"mov %1,%0\", xoperands);
	  return \"\";
	}
      return \"mov %1,%0\";
    case 5:
    case 6:
      return \"movhu %1,%0\";
    default:
      gcc_unreachable ();
    }
}"
  [(set_attr "cc" "none,clobber,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit")])

;; movsi and helpers

;; We use this to handle addition of two values when one operand is the
;; stack pointer and the other is a memory reference of some kind.  Reload
;; does not handle them correctly without this expander.
(define_expand "reload_insi"
  [(set (match_operand:SI 0 "register_operand" "=a")
	(match_operand:SI 1 "impossible_plus_operand" ""))
   (clobber (match_operand:SI 2 "register_operand" "=&r"))]
  ""
  "
{
  if (XEXP (operands[1], 0) == stack_pointer_rtx)
    {
      if (GET_CODE (XEXP (operands[1], 1)) == SUBREG
	  && (GET_MODE_SIZE (GET_MODE (XEXP (operands[1], 1)))
	      > GET_MODE_SIZE (GET_MODE (SUBREG_REG (XEXP (operands[1], 1))))))
	emit_move_insn (operands[2],
			gen_rtx_ZERO_EXTEND
			(GET_MODE (XEXP (operands[1], 1)),
			 SUBREG_REG (XEXP (operands[1], 1))));
      else
	emit_move_insn (operands[2], XEXP (operands[1], 1));
      emit_move_insn (operands[0], XEXP (operands[1], 0));
    }
  else
    {
      if (GET_CODE (XEXP (operands[1], 0)) == SUBREG
	  && (GET_MODE_SIZE (GET_MODE (XEXP (operands[1], 0)))
	      > GET_MODE_SIZE (GET_MODE (SUBREG_REG (XEXP (operands[1], 0))))))
	emit_move_insn (operands[2],
			gen_rtx_ZERO_EXTEND
			(GET_MODE (XEXP (operands[1], 0)),
			 SUBREG_REG (XEXP (operands[1], 0))));
      else
	emit_move_insn (operands[2], XEXP (operands[1], 0));
      emit_move_insn (operands[0], XEXP (operands[1], 1));
    }
  emit_insn (gen_addsi3 (operands[0], operands[0], operands[2]));
  DONE;
}")

(define_insn "pop_pic_reg"
  [(set (reg:SI PIC_REG)
	(mem:SI (post_inc:SI (reg:SI SP_REG))))]
  "reload_completed"
  "movm (sp),[a2]")

(define_expand "movsi"
  [(set (match_operand:SI 0 "general_operand" "")
	(match_operand:SI 1 "general_operand" ""))]
  ""
  "
{
  /* One of the ops has to be in a register */
  if (!register_operand (operand1, SImode)
      && !register_operand (operand0, SImode))
    operands[1] = copy_to_mode_reg (SImode, operand1);
  if (flag_pic)
    {
      rtx temp;
      if (SYMBOLIC_CONST_P (operands[1]))
	{
	  if (GET_CODE (operands[0]) == MEM)
	    operands[1] = force_reg (Pmode, operands[1]);
	  else
	    {
	      temp = (!can_create_pseudo_p ()
		      ? operands[0]
		      : gen_reg_rtx (Pmode));
	      operands[1] = legitimize_pic_address (operands[1], temp);
	    }
	}
      else if (GET_CODE (operands[1]) == CONST
	       && GET_CODE (XEXP (operands[1], 0)) == PLUS
	       && SYMBOLIC_CONST_P (XEXP (XEXP (operands[1], 0), 0)))
	{
	  temp = !can_create_pseudo_p () ? operands[0] : gen_reg_rtx (Pmode);
	  temp = legitimize_pic_address (XEXP (XEXP (operands[1], 0), 0),
					 temp);
	  operands[1] = expand_binop (SImode, add_optab, temp,
				      XEXP (XEXP (operands[1], 0), 1),
				      (!can_create_pseudo_p ()
				       ? temp
				       : gen_reg_rtx (Pmode)),
				      0, OPTAB_LIB_WIDEN);
	}
    }
}")

(define_insn ""
  [(set (match_operand:SI 0 "nonimmediate_operand"
				"=dx,ax,dx,a,dxm,dxm,axm,axm,dx,dx,ax,ax,axR,!*y,*f,*f,dxaQ")
	(match_operand:SI 1 "general_operand"
				"0,0,I,I,dx,ax,dx,ax,dixm,aixm,dixm,aixm,!*y,axR,0,dxaQi*f,*f"))]
  "register_operand (operands[0], SImode)
   || register_operand (operands[1], SImode)"
  "*
{
  switch (which_alternative)
    {
    case 0:
    case 1:
      return \"nop\";
    case 2:
      return \"clr %0\";
    case 3:
    case 4:
    case 5:
    case 6:
    case 7:
    case 8:
    case 9:
    case 10:
    case 11:
    case 12:
    case 13:
      if (GET_CODE (operands[1]) == CONST_DOUBLE)
	{
	  rtx xoperands[2];
	  xoperands[0] = operands[0];
	  xoperands[1] = GEN_INT (CONST_DOUBLE_LOW (operands[1]));
	  output_asm_insn (\"mov %1,%0\", xoperands);
	  return \"\";
	}

      if (REGNO_REG_CLASS (true_regnum (operands[0])) == EXTENDED_REGS
	  && GET_CODE (operands[1]) == CONST_INT)
	{
	  HOST_WIDE_INT val = INTVAL (operands[1]);

	  if (((val & 0x80) && ! (val & 0xffffff00))
	      || ((val & 0x800000) && ! (val & 0xff000000)))
	    return \"movu %1,%0\";
	}
      return \"mov %1,%0\";
    case 14:
      return \"nop\";
    case 15:
    case 16:
      return \"fmov %1,%0\";
    default:
      gcc_unreachable ();
    }
}"
  [(set_attr "cc" "none,none,clobber,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit,none,none_0hit,none_0hit")])

(define_expand "movsf"
  [(set (match_operand:SF 0 "general_operand" "")
	(match_operand:SF 1 "general_operand" ""))]
  ""
  "
{
  /* One of the ops has to be in a register */
  if (!register_operand (operand1, SFmode)
      && !register_operand (operand0, SFmode))
    operands[1] = copy_to_mode_reg (SFmode, operand1);
}")

(define_insn ""
  [(set (match_operand:SF 0 "nonimmediate_operand" "=f,dx,ax,dx,a,f,dxaQ,daxm,dax")
	(match_operand:SF 1 "general_operand" "0,0,0,G,G,fdxaQF,f,dax,daxFm"))]
  "register_operand (operands[0], SFmode)
   || register_operand (operands[1], SFmode)"
  "*
{
  switch (which_alternative)
    {
    case 0:
    case 1:
    case 2:
      return \"nop\";
    case 3:
      return \"clr %0\";
    /* case 4: below */
    case 5:
    case 6:
      return \"fmov %1, %0\";
    case 4:
    case 7:
    case 8:
      if (REGNO_REG_CLASS (true_regnum (operands[0])) == EXTENDED_REGS
	  && GET_CODE (operands[1]) == CONST_INT)
	{
	  HOST_WIDE_INT val = INTVAL (operands[1]);

	  if (((val & 0x80) && ! (val & 0xffffff00))
	      || ((val & 0x800000) && ! (val & 0xff000000)))
	    return \"movu %1,%0\";
	}
      return \"mov %1,%0\";
    default:
      gcc_unreachable ();
    }
}"
  [(set_attr "cc" "none,none,none,clobber,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit")])

(define_expand "movdi"
  [(set (match_operand:DI 0 "general_operand" "")
	(match_operand:DI 1 "general_operand" ""))]
  ""
  "
{
  /* One of the ops has to be in a register */
  if (!register_operand (operand1, DImode)
      && !register_operand (operand0, DImode))
    operands[1] = copy_to_mode_reg (DImode, operand1);
}")

(define_insn ""
  [(set (match_operand:DI 0 "nonimmediate_operand"
				"=dx,ax,dx,a,dxm,dxm,axm,axm,dx,dx,ax,ax,*f,*f,*f,dxa,*f,Q")
	(match_operand:DI 1 "general_operand"
				"0,0,I,I,dx,ax,dx,ax,dxim,axim,dxim,axim,0,*f,dxai,*f,Q,*f"))]
  "register_operand (operands[0], DImode)
   || register_operand (operands[1], DImode)"
  "*
{
  long val[2];
  REAL_VALUE_TYPE rv;

  switch (which_alternative)
    {
      case 0:
      case 1:
	return \"nop\";

      case 2:
	return \"clr %L0\;clr %H0\";

      case 3:
	if (rtx_equal_p (operands[0], operands[1]))
	  return \"sub %L1,%L0\;mov %L0,%H0\";
	else
	  return \"mov %1,%L0\;mov %L0,%H0\";
      case 4:
      case 5:
      case 6:
      case 7:
      case 8:
      case 9:
      case 10:
      case 11:
	if (GET_CODE (operands[1]) == CONST_INT)
	  {
	    rtx low, high;
	    split_double (operands[1], &low, &high);
	    val[0] = INTVAL (low);
	    val[1] = INTVAL (high);
	  }
	if (GET_CODE (operands[1]) == CONST_DOUBLE)
	  {
	    if (GET_MODE (operands[1]) == DFmode)
	      {
		REAL_VALUE_FROM_CONST_DOUBLE (rv, operands[1]);
		REAL_VALUE_TO_TARGET_DOUBLE (rv, val);
	      }
	    else if (GET_MODE (operands[1]) == VOIDmode
		     || GET_MODE (operands[1]) == DImode)
	      {
		val[0] = CONST_DOUBLE_LOW (operands[1]);
		val[1] = CONST_DOUBLE_HIGH (operands[1]);
	      }
	  }

	if (GET_CODE (operands[1]) == MEM
	    && reg_overlap_mentioned_p (operands[0], XEXP (operands[1], 0)))
	  {
	    rtx temp = operands[0];

	    while (GET_CODE (temp) == SUBREG)
	      temp = SUBREG_REG (temp);

	    gcc_assert (GET_CODE (temp) == REG);

	    if (reg_overlap_mentioned_p (gen_rtx_REG (SImode, REGNO (temp)),
					 XEXP (operands[1], 0)))
	      return \"mov %H1,%H0\;mov %L1,%L0\";
	    else
	      return \"mov %L1,%L0\;mov %H1,%H0\";

	  }
	else if (GET_CODE (operands[1]) == MEM
		 && CONSTANT_ADDRESS_P (XEXP (operands[1], 0))
		 && REGNO_REG_CLASS (REGNO (operands[0])) == ADDRESS_REGS)
	  {
	    rtx xoperands[2];

	    xoperands[0] = operands[0];
	    xoperands[1] = XEXP (operands[1], 0);

	    output_asm_insn (\"mov %1,%L0\;mov (4,%L0),%H0\;mov (%L0),%L0\",
			     xoperands);
	    return \"\";
	  }
	else
	  {
	    if ((GET_CODE (operands[1]) == CONST_INT
		 || GET_CODE (operands[1]) == CONST_DOUBLE)
		&& val[0] == 0)
	      {
		if (REGNO_REG_CLASS (REGNO (operands[0])) == DATA_REGS)
		  output_asm_insn (\"clr %L0\", operands);
		else
		  output_asm_insn (\"mov %L1,%L0\", operands);
	      }
	    else if ((GET_CODE (operands[1]) == CONST_INT
		      || GET_CODE (operands[1]) == CONST_DOUBLE)
		     && (REGNO_REG_CLASS (true_regnum (operands[0]))
			 == EXTENDED_REGS)
		     && (((val[0] & 0x80) && ! (val[0] & 0xffffff00))
			 || ((val[0] & 0x800000) && ! (val[0] & 0xff000000))))
	      output_asm_insn (\"movu %L1,%L0\", operands);
	    else
	      output_asm_insn (\"mov %L1,%L0\", operands);

	    if ((GET_CODE (operands[1]) == CONST_INT
		 || GET_CODE (operands[1]) == CONST_DOUBLE)
		&& val[1] == 0)
	      {
		if (REGNO_REG_CLASS (REGNO (operands[0])) == DATA_REGS)
		  output_asm_insn (\"clr %H0\", operands);
		else
		  output_asm_insn (\"mov %H1,%H0\", operands);
	      }
	    else if ((GET_CODE (operands[1]) == CONST_INT
		      || GET_CODE (operands[1]) == CONST_DOUBLE)
		     && val[0] == val[1])
	      output_asm_insn (\"mov %L0,%H0\", operands);
	    else if ((GET_CODE (operands[1]) == CONST_INT
		      || GET_CODE (operands[1]) == CONST_DOUBLE)
		     && (REGNO_REG_CLASS (true_regnum (operands[0]))
			 == EXTENDED_REGS)
		     && (((val[1] & 0x80) && ! (val[1] & 0xffffff00))
			 || ((val[1] & 0x800000) && ! (val[1] & 0xff000000))))
	      output_asm_insn (\"movu %H1,%H0\", operands);
	    else
	      output_asm_insn (\"mov %H1,%H0\", operands);
	    return \"\";
	  }
      case 12:
        return \"nop\";
      case 13:
      case 14:
      case 15:
        return \"fmov %L1, %L0\;fmov %H1, %H0\";
      case 16:
	if (GET_CODE (operands[1]) == MEM
	    && GET_CODE (XEXP (operands[1], 0)) == CONST_INT
	    && (INTVAL (XEXP (operands[1], 0)) & 7) == 0)
	  return \"fmov %D1, %D0\";
	else
          return \"fmov %L1, %L0\;fmov %H1, %H0\";
      case 17:
	if (GET_CODE (operands[0]) == MEM
	    && GET_CODE (XEXP (operands[0], 0)) == CONST_INT
	    && (INTVAL (XEXP (operands[0], 0)) & 7) == 0)
	  return \"fmov %D1, %D0\";
	else
          return \"fmov %L1, %L0\;fmov %H1, %H0\";
    default:
      gcc_unreachable ();
    }
}"
  [(set (attr "cc")
	(cond
	 [
	 (ior (lt (symbol_ref "which_alternative") (const_int 2))
	      (eq (symbol_ref "which_alternative") (const_int 12))
	      ) (const_string "none")
	 (eq (symbol_ref "which_alternative") (const_int 2)
	     ) (const_string "clobber")
	 (eq (symbol_ref "which_alternative") (const_int 3)
	     ) (if_then_else
		(ne (symbol_ref "rtx_equal_p (operands[0], operands[1])")
		    (const_int 0)) (const_string "clobber")
		    (const_string "none_0hit"))
	 (ior (eq (symbol_ref "which_alternative") (const_int 8))
	      (eq (symbol_ref "which_alternative") (const_int 9))
	      ) (if_then_else
		 (ne (symbol_ref "mn10300_wide_const_load_uses_clr
				  (operands)")
		     (const_int 0)) (const_string "clobber")
		     (const_string "none_0hit"))
	 ] (const_string "none_0hit")))])

(define_expand "movdf"
  [(set (match_operand:DF 0 "general_operand" "")
	(match_operand:DF 1 "general_operand" ""))]
  ""
  "
{
  /* One of the ops has to be in a register */
  if (!register_operand (operand1, DFmode)
      && !register_operand (operand0, DFmode))
    operands[1] = copy_to_mode_reg (DFmode, operand1);
}")

(define_insn ""
  [(set (match_operand:DF 0 "nonimmediate_operand"
				"=f,dx,ax,dx,f,f,dxa,f,Q,a,dxm,dxm,axm,axm,dx,dx,ax,ax")
	(match_operand:DF 1 "general_operand"
				"0,0,0,G,f,dxaF,f,Q,f,G,dx,ax,dx,ax,dxFm,axFm,dxFm,axFm"))]
  "register_operand (operands[0], DFmode)
   || register_operand (operands[1], DFmode)"
  "*
{
  long val[2];
  REAL_VALUE_TYPE rv;

  switch (which_alternative)
    {
      case 0:
      case 1:
      case 2:
	return \"nop\";

      case 3:
	return \"clr %L0\;clr %H0\";

      case 4:
      case 5:
      case 6:
        return \"fmov %L1, %L0\;fmov %H1, %H0\";

      case 7:
	if (GET_CODE (operands[1]) == MEM
	    && GET_CODE (XEXP (operands[1], 0)) == CONST_INT
	    && (INTVAL (XEXP (operands[1], 0)) & 7) == 0)
	  return \"fmov %D1, %D0\";
	else
          return \"fmov %L1, %L0\;fmov %H1, %H0\";

      case 8:
	if (GET_CODE (operands[0]) == MEM
	    && GET_CODE (XEXP (operands[0], 0)) == CONST_INT
	    && (INTVAL (XEXP (operands[0], 0)) & 7) == 0)
	  return \"fmov %D1, %D0\";
	else
          return \"fmov %L1, %L0\;fmov %H1, %H0\";

      case 9:
	 if (rtx_equal_p (operands[0], operands[1]))
	   return \"sub %L1,%L0\;mov %L0,%H0\";
	 else
	   return \"mov %1,%L0\;mov %L0,%H0\";
      case 10:
      case 11:
      case 12:
      case 13:
      case 14:
      case 15:
      case 16:
      case 17:
	if (GET_CODE (operands[1]) == CONST_INT)
	  {
	    rtx low, high;
	    split_double (operands[1], &low, &high);
	    val[0] = INTVAL (low);
	    val[1] = INTVAL (high);
	  }
	if (GET_CODE (operands[1]) == CONST_DOUBLE)
	  {
	    if (GET_MODE (operands[1]) == DFmode)
	      {
		REAL_VALUE_FROM_CONST_DOUBLE (rv, operands[1]);
		REAL_VALUE_TO_TARGET_DOUBLE (rv, val);
	      }
	    else if (GET_MODE (operands[1]) == VOIDmode
		     || GET_MODE (operands[1]) == DImode)
	      {
		val[0] = CONST_DOUBLE_LOW (operands[1]);
		val[1] = CONST_DOUBLE_HIGH (operands[1]);
	      }
	  }

	if (GET_CODE (operands[1]) == MEM
	    && reg_overlap_mentioned_p (operands[0], XEXP (operands[1], 0)))
	  {
	    rtx temp = operands[0];

	    while (GET_CODE (temp) == SUBREG)
	      temp = SUBREG_REG (temp);

	    gcc_assert (GET_CODE (temp) == REG);

	    if (reg_overlap_mentioned_p (gen_rtx_REG (SImode, REGNO (temp)),
					 XEXP (operands[1], 0)))
	      return \"mov %H1,%H0\;mov %L1,%L0\";
	    else
	      return \"mov %L1,%L0\;mov %H1,%H0\";

	  }
	else if (GET_CODE (operands[1]) == MEM
		 && CONSTANT_ADDRESS_P (XEXP (operands[1], 0))
		 && REGNO_REG_CLASS (REGNO (operands[0])) == ADDRESS_REGS)
	  {
	    rtx xoperands[2];

	    xoperands[0] = operands[0];
	    xoperands[1] = XEXP (operands[1], 0);

	    output_asm_insn (\"mov %1,%L0\;mov (4,%L0),%H0\;mov (%L0),%L0\",
			     xoperands);
	    return \"\";
	  }
	else
	  {
	    if ((GET_CODE (operands[1]) == CONST_INT
		 || GET_CODE (operands[1]) == CONST_DOUBLE)
		&& val[0] == 0)
	      {
		if (REGNO_REG_CLASS (REGNO (operands[0])) == DATA_REGS)
		  output_asm_insn (\"clr %L0\", operands);
		else
		  output_asm_insn (\"mov %L1,%L0\", operands);
	      }
	    else if ((GET_CODE (operands[1]) == CONST_INT
		      || GET_CODE (operands[1]) == CONST_DOUBLE)
		     && (REGNO_REG_CLASS (true_regnum (operands[0]))
			 == EXTENDED_REGS)
		     && (((val[0] & 0x80) && ! (val[0] & 0xffffff00))
			 || ((val[0] & 0x800000) && ! (val[0] & 0xff000000))))
	      output_asm_insn (\"movu %L1,%L0\", operands);
	    else
	      output_asm_insn (\"mov %L1,%L0\", operands);

	    if ((GET_CODE (operands[1]) == CONST_INT
		 || GET_CODE (operands[1]) == CONST_DOUBLE)
		&& val[1] == 0)
	      {
		if (REGNO_REG_CLASS (REGNO (operands[0])) == DATA_REGS)
		  output_asm_insn (\"clr %H0\", operands);
		else
		  output_asm_insn (\"mov %H1,%H0\", operands);
	      }
	    else if ((GET_CODE (operands[1]) == CONST_INT
		      || GET_CODE (operands[1]) == CONST_DOUBLE)
		     && val[0] == val[1])
	      output_asm_insn (\"mov %L0,%H0\", operands);
	    else if ((GET_CODE (operands[1]) == CONST_INT
		      || GET_CODE (operands[1]) == CONST_DOUBLE)
		     && (REGNO_REG_CLASS (true_regnum (operands[0]))
			 == EXTENDED_REGS)
		     && (((val[1] & 0x80) && ! (val[1] & 0xffffff00))
			 || ((val[1] & 0x800000) && ! (val[1] & 0xff000000))))
	      output_asm_insn (\"movu %H1,%H0\", operands);
	    else
	      output_asm_insn (\"mov %H1,%H0\", operands);
	    return \"\";
	  }
    default:
      gcc_unreachable ();
    }
}"
  [(set (attr "cc")
	(cond
	 [
	 (lt (symbol_ref "which_alternative") (const_int 3)
	     ) (const_string "none")
	 (eq (symbol_ref "which_alternative") (const_int 3)
	     ) (const_string "clobber")
	 (eq (symbol_ref "which_alternative") (const_int 9)
	     ) (if_then_else
		(ne (symbol_ref "rtx_equal_p (operands[0], operands[1])")
		    (const_int 0)) (const_string "clobber")
		    (const_string "none_0hit"))
	 (ior (eq (symbol_ref "which_alternative") (const_int 14))
	      (eq (symbol_ref "which_alternative") (const_int 15))
	      ) (if_then_else
		 (ne (symbol_ref "mn10300_wide_const_load_uses_clr
				  (operands)")
		     (const_int 0)) (const_string "clobber")
		     (const_string "none_0hit"))
	 ] (const_string "none_0hit")))])



;; ----------------------------------------------------------------------
;; TEST INSTRUCTIONS
;; ----------------------------------------------------------------------

(define_insn "*tst_extqisi_am33"
  [(set (cc0) (compare
	       (zero_extend:SI (match_operand:QI 0 "memory_operand" "dx,!a"))
	       (const_int 0)))]
  "TARGET_AM33"
  "* return output_tst (operands[0], insn);"
  [(set_attr "cc" "set_znv")])

(define_insn "*tst_extqisi"
  [(set (cc0) (compare
	       (zero_extend:SI (match_operand:QI 0 "memory_operand" "dx"))
	       (const_int 0)))]
  ""
  "* return output_tst (operands[0], insn);"
  [(set_attr "cc" "set_znv")])

(define_insn "*tst_exthisi_am33"
  [(set (cc0) (compare
	       (zero_extend:SI (match_operand:HI 0 "memory_operand" "dx,!a"))
	       (const_int 0)))]
  "TARGET_AM33"
  "* return output_tst (operands[0], insn);"
  [(set_attr "cc" "set_znv")])

(define_insn "*tst_exthisi"
  [(set (cc0) (compare
	       (zero_extend:SI (match_operand:HI 0 "memory_operand" "dx"))
	       (const_int 0)))]
  ""
  "* return output_tst (operands[0], insn);"
  [(set_attr "cc" "set_znv")])

;; Ordinarily, the cmp instruction will set the Z bit of cc0 to 1 if
;; its operands hold equal values, but the operands of a cmp
;; instruction must be distinct registers.  In the case where we'd
;; like to compare a register to itself, we can achieve this effect
;; with a btst 0,d0 instead.  (This will not alter the contents of d0
;; but will have the proper effect on cc0.  Using d0 is arbitrary; any
;; data register would work.)

;; Even though the first alternative would be preferable if it can
;; possibly match, reload must not be given the opportunity to attempt
;; to use it.  It assumes that such matches can only occur when one of
;; the operands is used for input and the other for output.  Since
;; this is not the case, it abort()s.  Indeed, such a reload cannot be
;; possibly satisfied, so just mark the alternative with a `!', so
;; that it is not considered by reload.

(define_insn "*cmpsi"
  [(set (cc0)
	(compare (match_operand:SI 0 "register_operand" "!*d*a*x,dax,dax")
		 (match_operand:SI 1 "nonmemory_operand" "*0,I,daxi")))]
  ""
  "*
{
  if (which_alternative == 0)
    return \"btst 0,d0\";
  if (which_alternative == 1)
    return output_tst (operands[0], insn);
  return \"cmp %1,%0\";
}"
  [(set_attr "cc" "compare,set_znv,compare")])

(define_insn "*cmpsf"
  [(set (cc0)
	(compare (match_operand:SF 0 "register_operand" "f,f")
		 (match_operand:SF 1 "nonmemory_operand" "f,F")))]
  "TARGET_AM33_2"
  "fcmp %1,%0"
  [(set_attr "cc" "compare,compare")])

;; ----------------------------------------------------------------------
;; ADD INSTRUCTIONS
;; ----------------------------------------------------------------------

(define_expand "addsi3"
  [(set (match_operand:SI 0 "register_operand" "")
	(plus:SI (match_operand:SI 1 "register_operand" "")
		 (match_operand:SI 2 "nonmemory_operand" "")))]
  ""
  "")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=dx,a,x,a,dax,!*y,!dax")
	(plus:SI (match_operand:SI 1 "register_operand" "%0,0,0,0,0,0,dax")
		 (match_operand:SI 2 "nonmemory_operand" "J,J,L,L,daxi,i,dax")))]
  "TARGET_AM33"
  "*
{
  switch (which_alternative)
    {
    case 0:
    case 1:
      return \"inc %0\";
    case 2:
    case 3:
      return \"inc4 %0\";
    case 4:
    case 5:
      return \"add %2,%0\";
    case 6:
      {
	enum reg_class src1_class, src2_class, dst_class;

	src1_class = REGNO_REG_CLASS (true_regnum (operands[1]));
	src2_class = REGNO_REG_CLASS (true_regnum (operands[2]));
	dst_class = REGNO_REG_CLASS (true_regnum (operands[0]));

	/* I'm not sure if this can happen or not.  Might as well be prepared
	  and generate the best possible code if it does happen.  */
	if (true_regnum (operands[0]) == true_regnum (operands[1]))
	  return \"add %2,%0\";
	if (true_regnum (operands[0]) == true_regnum (operands[2]))
	  return \"add %1,%0\";

	/* Catch cases where no extended register was used.  These should be
	   handled just like the mn10300.  */
	if (src1_class != EXTENDED_REGS
	    && src2_class != EXTENDED_REGS
	    && dst_class != EXTENDED_REGS)
	  {
	    /* We have to copy one of the sources into the destination, then
	       add the other source to the destination.

	       Carefully select which source to copy to the destination; a naive
	       implementation will waste a byte when the source classes are
	       different and the destination is an address register.  Selecting
	       the lowest cost register copy will optimize this sequence.  */
	    if (REGNO_REG_CLASS (true_regnum (operands[1]))
		== REGNO_REG_CLASS (true_regnum (operands[0])))
	      return \"mov %1,%0\;add %2,%0\";
	    return \"mov %2,%0\;add %1,%0\";
	  }

	/* At least one register is an extended register.  */

	/* The three operand add instruction on the am33 is a win iff the
	   output register is an extended register, or if both source
	   registers are extended registers.  */
	if (dst_class == EXTENDED_REGS
	    || src1_class == src2_class)
	  return \"add %2,%1,%0\";

      /* It is better to copy one of the sources to the destination, then
	 perform a 2 address add.  The destination in this case must be
	 an address or data register and one of the sources must be an
	 extended register and the remaining source must not be an extended
	 register.

	 The best code for this case is to copy the extended reg to the
	 destination, then emit a two address add.  */
      if (src1_class == EXTENDED_REGS)
	return \"mov %1,%0\;add %2,%0\";
      return \"mov %2,%0\;add %1,%0\";
      }
    default:
      gcc_unreachable ();
    }
}"
  [(set_attr "cc" "set_zn,none_0hit,set_zn,none_0hit,set_zn,none_0hit,set_zn")])

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=dx,a,a,dax,!*y,!dax")
	(plus:SI (match_operand:SI 1 "register_operand" "%0,0,0,0,0,dax")
		 (match_operand:SI 2 "nonmemory_operand" "J,J,L,daxi,i,dax")))]
  ""
  "*
{
  switch (which_alternative)
    {
    case 0:
    case 1:
      return \"inc %0\";
    case 2:
      return \"inc4 %0\";
    case 3:
    case 4:
      return \"add %2,%0\";
    case 5:
      /* I'm not sure if this can happen or not.  Might as well be prepared
	 and generate the best possible code if it does happen.  */
      if (true_regnum (operands[0]) == true_regnum (operands[1]))
	return \"add %2,%0\";
      if (true_regnum (operands[0]) == true_regnum (operands[2]))
	return \"add %1,%0\";

      /* We have to copy one of the sources into the destination, then add
	 the other source to the destination.

	 Carefully select which source to copy to the destination; a naive
	 implementation will waste a byte when the source classes are different
	 and the destination is an address register.  Selecting the lowest
	 cost register copy will optimize this sequence.  */
      if (REGNO_REG_CLASS (true_regnum (operands[1]))
	  == REGNO_REG_CLASS (true_regnum (operands[0])))
	return \"mov %1,%0\;add %2,%0\";
      return \"mov %2,%0\;add %1,%0\";
    default:
      gcc_unreachable ();
    }
}"
  [(set_attr "cc" "set_zn,none_0hit,none_0hit,set_zn,none_0hit,set_zn")])

;; ----------------------------------------------------------------------
;; SUBTRACT INSTRUCTIONS
;; ----------------------------------------------------------------------

(define_expand "subsi3"
  [(set (match_operand:SI 0 "register_operand" "")
	(minus:SI (match_operand:SI 1 "register_operand" "")
		  (match_operand:SI 2 "nonmemory_operand" "")))]
  ""
  "")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=dax,!dax")
	(minus:SI (match_operand:SI 1 "register_operand" "0,dax")
		  (match_operand:SI 2 "nonmemory_operand" "daxi,dax")))]
  "TARGET_AM33"
  "*
{
  if (true_regnum (operands[0]) == true_regnum (operands[1]))
    return \"sub %2,%0\";
  else
    {
      enum reg_class src1_class, src2_class, dst_class;

      src1_class = REGNO_REG_CLASS (true_regnum (operands[1]));
      src2_class = REGNO_REG_CLASS (true_regnum (operands[2]));
      dst_class = REGNO_REG_CLASS (true_regnum (operands[0]));

      /* If no extended registers are used, then the best way to handle
	 this is to copy the first source operand into the destination
	 and emit a two address subtraction.  */
      if (src1_class != EXTENDED_REGS
	  && src2_class != EXTENDED_REGS
	  && dst_class != EXTENDED_REGS
	  && true_regnum (operands[0]) != true_regnum (operands[2]))
	return \"mov %1,%0\;sub %2,%0\";
      return \"sub %2,%1,%0\";
    }
}"
  [(set_attr "cc" "set_zn")])

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=dax")
	(minus:SI (match_operand:SI 1 "register_operand" "0")
		  (match_operand:SI 2 "nonmemory_operand" "daxi")))]
  ""
  "sub %2,%0"
  [(set_attr "cc" "set_zn")])

(define_expand "negsi2"
  [(set (match_operand:SI 0 "register_operand" "")
        (neg:SI (match_operand:SI 1 "register_operand" "")))]
  ""
  "
{
  rtx target = gen_reg_rtx (SImode);

  emit_move_insn (target, const0_rtx);
  emit_insn (gen_subsi3 (target, target, operands[1]));
  emit_move_insn (operands[0], target);
  DONE;
}")

;; ----------------------------------------------------------------------
;; MULTIPLY INSTRUCTIONS
;; ----------------------------------------------------------------------

(define_insn "mulsidi3"
  [(set (match_operand:DI 0 "register_operand" "=dax")
        (mult:DI (sign_extend:DI (match_operand:SI 1 "register_operand" "dax"))
                 (sign_extend:DI (match_operand:SI 2 "register_operand" "dax"))))]
  "TARGET_AM33"
  "mul %1,%2,%H0,%L0"
  [(set_attr "cc" "set_zn")])

(define_insn "umulsidi3"
  [(set (match_operand:DI 0 "register_operand" "=dax")
        (mult:DI (zero_extend:DI (match_operand:SI 1 "register_operand" "dax"))
                 (zero_extend:DI (match_operand:SI 2 "register_operand" "dax"))))]
  "TARGET_AM33"
  "mulu %1,%2,%H0,%L0"
  [(set_attr "cc" "set_zn")])

(define_expand "mulsi3"
  [(set (match_operand:SI 0 "register_operand" "")
	(mult:SI (match_operand:SI 1 "register_operand" "")
		 (match_operand:SI 2 "register_operand" "")))]
  ""
  "")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=dx,!dax")
	(mult:SI (match_operand:SI 1 "register_operand" "%0,0")
		 (match_operand:SI 2 "nonmemory_operand" "dx,daxi")))]
  "TARGET_AM33"
  "*
{
  if (TARGET_MULT_BUG)
    return \"nop\;nop\;mul %2,%0\";
  else
    return \"mul %2,%0\";
}"
  [(set_attr "cc" "set_zn")])

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=dx")
	(mult:SI (match_operand:SI 1 "register_operand" "%0")
		 (match_operand:SI 2 "register_operand" "dx")))]
  ""
  "*
{
  if (TARGET_MULT_BUG)
    return \"nop\;nop\;mul %2,%0\";
  else
    return \"mul %2,%0\";
}"
  [(set_attr "cc" "set_zn")])

(define_insn "udivmodsi4"
  [(set (match_operand:SI 0 "nonimmediate_operand" "=dx")
	(udiv:SI (match_operand:SI 1 "general_operand" "0")
		 (match_operand:SI 2 "general_operand" "dx")))
   (set (match_operand:SI 3 "nonimmediate_operand" "=&d")
	(umod:SI (match_dup 1) (match_dup 2)))]
  ""
  "*
{
  output_asm_insn (\"sub %3,%3\;mov %3,mdr\", operands);

  if (find_reg_note (insn, REG_UNUSED, operands[3]))
    return \"divu %2,%0\";
  else
    return \"divu %2,%0\;mov mdr,%3\";
}"
  [(set_attr "cc" "set_zn")])

(define_insn "divmodsi4"
  [(set (match_operand:SI 0 "nonimmediate_operand" "=dx")
	(div:SI (match_operand:SI 1 "general_operand" "0")
		 (match_operand:SI 2 "general_operand" "dx")))
   (set (match_operand:SI 3 "nonimmediate_operand" "=d")
	(mod:SI (match_dup 1) (match_dup 2)))]
  ""
  "*
{
  if (find_reg_note (insn, REG_UNUSED, operands[3]))
    return \"ext %0\;div %2,%0\";
  else
    return \"ext %0\;div %2,%0\;mov mdr,%3\";
}"
  [(set_attr "cc" "set_zn")])


;; ----------------------------------------------------------------------
;; AND INSTRUCTIONS
;; ----------------------------------------------------------------------

(define_expand "andsi3"
  [(set (match_operand:SI 0 "register_operand" "")
	(and:SI (match_operand:SI 1 "register_operand" "")
		(match_operand:SI 2 "nonmemory_operand" "")))]
  ""
  "")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=dx,dx,!dax")
	(and:SI (match_operand:SI 1 "register_operand" "%0,0,dax")
		(match_operand:SI 2 "nonmemory_operand" "N,dxi,dax")))]
  "TARGET_AM33"
  "*
{
  if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 0xff)
    return \"extbu %0\";
  if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 0xffff)
    return \"exthu %0\";
  if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 0x7fffffff)
    return \"add %0,%0\;lsr 1,%0\";
  if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 0x3fffffff)
    return \"asl2 %0\;lsr 2,%0\";
  if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 0x1fffffff)
    return \"add %0,%0\;asl2 %0\;lsr 3,%0\";
  if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 0x0fffffff)
    return \"asl2 %0\;asl2 %0\;lsr 4,%0\";
  if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 0xfffffffe)
    return \"lsr 1,%0\;add %0,%0\";
  if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 0xfffffffc)
    return \"lsr 2,%0\;asl2 %0\";
  if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 0xfffffff8)
    return \"lsr 3,%0\;add %0,%0\;asl2 %0\";
  if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 0xfffffff0)
    return \"lsr 4,%0\;asl2 %0\;asl2 %0\";
  if (REG_P (operands[2]) && REG_P (operands[1])
      && true_regnum (operands[0]) != true_regnum (operands[1])
      && true_regnum (operands[0]) != true_regnum (operands[2])
      && REGNO_REG_CLASS (true_regnum (operands[0])) == DATA_REGS
      && REGNO_REG_CLASS (true_regnum (operands[1])) == DATA_REGS
      && REGNO_REG_CLASS (true_regnum (operands[2])) == DATA_REGS)
    return \"mov %1,%0\;and %2,%0\";
  if (REG_P (operands[2]) && REG_P (operands[1])
      && true_regnum (operands[0]) != true_regnum (operands[1])
      && true_regnum (operands[0]) != true_regnum (operands[2]))
    return \"and %1,%2,%0\";
  if (REG_P (operands[2]) && REG_P (operands[0])
      && true_regnum (operands[2]) == true_regnum (operands[0]))
    return \"and %1,%0\";
  return \"and %2,%0\";
}"
  [(set (attr "cc")
	(cond
	 [
	 (eq (symbol_ref "which_alternative") (const_int 0)
	     ) (const_string "none_0hit")
	 (ne (symbol_ref "GET_CODE (operands[2]) == CONST_INT
			  && (INTVAL (operands[2]) == 0x7fffffff
			      || INTVAL (operands[2]) == 0x3fffffff
			      || INTVAL (operands[2]) == 0x1fffffff
			      || INTVAL (operands[2]) == 0x0fffffff
			      || INTVAL (operands[2]) == 0xfffffffe
			      || INTVAL (operands[2]) == 0xfffffffc
			      || INTVAL (operands[2]) == 0xfffffff8
			      || INTVAL (operands[2]) == 0xfffffff0)")
	     (const_int 0)) (const_string "set_zn")
	  ] (const_string "set_znv")))])

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=dx,dx")
	(and:SI (match_operand:SI 1 "register_operand" "%0,0")
		(match_operand:SI 2 "nonmemory_operand" "N,dxi")))]
  ""
  "*
{
  if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 0xff)
    return \"extbu %0\";
  if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 0xffff)
    return \"exthu %0\";
  if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 0x7fffffff)
    return \"add %0,%0\;lsr 1,%0\";
  if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 0x3fffffff)
    return \"asl2 %0\;lsr 2,%0\";
  if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 0x1fffffff)
    return \"add %0,%0\;asl2 %0\;lsr 3,%0\";
  if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 0x0fffffff)
    return \"asl2 %0\;asl2 %0\;lsr 4,%0\";
  if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 0xfffffffe)
    return \"lsr 1,%0\;add %0,%0\";
  if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 0xfffffffc)
    return \"lsr 2,%0\;asl2 %0\";
  if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 0xfffffff8)
    return \"lsr 3,%0\;add %0,%0\;asl2 %0\";
  if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 0xfffffff0)
    return \"lsr 4,%0\;asl2 %0\;asl2 %0\";
  return \"and %2,%0\";
}"
  [(set (attr "cc")
	(cond
	 [
	 (eq (symbol_ref "which_alternative") (const_int 0)
	     ) (const_string "none_0hit")
	 ;; Shifts don't set the V flag, but bitwise operations clear
	 ;; it (which correctly reflects the absence of overflow in a
	 ;; compare-with-zero that might follow).  As for the
	 ;; 0xfffffffe case, the add may overflow, so we can't use the
	 ;; V flag.
	 (ne (symbol_ref "GET_CODE (operands[2]) == CONST_INT
			  && (INTVAL (operands[2]) == 0x7fffffff
			      || INTVAL (operands[2]) == 0x3fffffff
			      || INTVAL (operands[2]) == 0x1fffffff
			      || INTVAL (operands[2]) == 0x0fffffff
			      || INTVAL (operands[2]) == 0xfffffffe
			      || INTVAL (operands[2]) == 0xfffffffc
			      || INTVAL (operands[2]) == 0xfffffff8
			      || INTVAL (operands[2]) == 0xfffffff0)")
	     (const_int 0)) (const_string "set_zn")
	  ] (const_string "set_znv")))])

;; ----------------------------------------------------------------------
;; OR INSTRUCTIONS
;; ----------------------------------------------------------------------

(define_expand "iorsi3"
  [(set (match_operand:SI 0 "register_operand" "")
	(ior:SI (match_operand:SI 1 "register_operand" "")
		(match_operand:SI 2 "nonmemory_operand" "")))]
  ""
  "")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=dx,!dax")
	(ior:SI (match_operand:SI 1 "register_operand" "%0,dax")
		(match_operand:SI 2 "nonmemory_operand" "dxi,dax")))]
  "TARGET_AM33"
  "*
{
  if (REG_P (operands[2]) && REG_P (operands[1])
      && true_regnum (operands[0]) != true_regnum (operands[1])
      && true_regnum (operands[0]) != true_regnum (operands[2])
      && REGNO_REG_CLASS (true_regnum (operands[0])) == DATA_REGS
      && REGNO_REG_CLASS (true_regnum (operands[1])) == DATA_REGS
      && REGNO_REG_CLASS (true_regnum (operands[2])) == DATA_REGS)
    return \"mov %1,%0\;or %2,%0\";
  if (REG_P (operands[2]) && REG_P (operands[1])
      && true_regnum (operands[0]) != true_regnum (operands[1])
      && true_regnum (operands[0]) != true_regnum (operands[2]))
    return \"or %1,%2,%0\";
  if (REG_P (operands[2]) && REG_P (operands[0])
      && true_regnum (operands[2]) == true_regnum (operands[0]))
    return \"or %1,%0\";
  return \"or %2,%0\";
}"
  [(set_attr "cc" "set_znv")])

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=dx")
	(ior:SI (match_operand:SI 1 "register_operand" "%0")
		(match_operand:SI 2 "nonmemory_operand" "dxi")))]
  ""
  "or %2,%0"
  [(set_attr "cc" "set_znv")])

;; ----------------------------------------------------------------------
;; XOR INSTRUCTIONS
;; ----------------------------------------------------------------------

(define_expand "xorsi3"
  [(set (match_operand:SI 0 "register_operand" "")
	(xor:SI (match_operand:SI 1 "register_operand" "")
		(match_operand:SI 2 "nonmemory_operand" "")))]
  ""
  "")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=dx,!dax")
	(xor:SI (match_operand:SI 1 "register_operand" "%0,dax")
		(match_operand:SI 2 "nonmemory_operand" "dxi,dax")))]
  "TARGET_AM33"
  "*
{
  if (REG_P (operands[2]) && REG_P (operands[1])
      && true_regnum (operands[0]) != true_regnum (operands[1])
      && true_regnum (operands[0]) != true_regnum (operands[2])
      && REGNO_REG_CLASS (true_regnum (operands[0])) == DATA_REGS
      && REGNO_REG_CLASS (true_regnum (operands[1])) == DATA_REGS
      && REGNO_REG_CLASS (true_regnum (operands[2])) == DATA_REGS)
    return \"mov %1,%0\;xor %2,%0\";
  if (REG_P (operands[2]) && REG_P (operands[1])
      && true_regnum (operands[0]) != true_regnum (operands[1])
      && true_regnum (operands[0]) != true_regnum (operands[2]))
    return \"xor %1,%2,%0\";
  if (REG_P (operands[2]) && REG_P (operands[0])
      && true_regnum (operands[2]) == true_regnum (operands[0]))
    return \"xor %1,%0\";
  return \"xor %2,%0\";
}"
  [(set_attr "cc" "set_znv")])

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=dx")
	(xor:SI (match_operand:SI 1 "register_operand" "%0")
		(match_operand:SI 2 "nonmemory_operand" "dxi")))]
  ""
  "xor %2,%0"
  [(set_attr "cc" "set_znv")])

;; ----------------------------------------------------------------------
;; NOT INSTRUCTIONS
;; ----------------------------------------------------------------------

(define_expand "one_cmplsi2"
  [(set (match_operand:SI 0 "register_operand" "")
	(not:SI (match_operand:SI 1 "register_operand" "")))]
  ""
  "")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=dx,!dax")
	(not:SI (match_operand:SI 1 "register_operand" "0,0")))]
  "TARGET_AM33"
  "not %0"
  [(set_attr "cc" "set_znv")])

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=dx")
	(not:SI (match_operand:SI 1 "register_operand" "0")))]
  ""
  "not %0"
  [(set_attr "cc" "set_znv")])

;; -----------------------------------------------------------------
;; BIT FIELDS
;; -----------------------------------------------------------------


;; These set/clear memory in byte sized chunks.
;;
;; They are no smaller/faster than loading the value into a register
;; and storing the register, but they don't need a scratch register
;; which may allow for better code generation.
(define_insn ""
  [(set (match_operand:QI 0 "nonimmediate_operand" "=R,d") (const_int 0))]
  ""
  "@
  bclr 255,%A0
  clr %0"
  [(set_attr "cc" "clobber")])

(define_insn ""
  [(set (match_operand:QI 0 "nonimmediate_operand" "=R,d") (const_int -1))]
  ""
  "@
  bset 255,%A0
  mov -1,%0"
  [(set_attr "cc" "clobber,none_0hit")])

(define_insn ""
  [(set (match_operand:QI 0 "nonimmediate_operand" "+R,d")
	(subreg:QI
	  (and:SI (subreg:SI (match_dup 0) 0)
		  (match_operand:SI 1 "const_int_operand" "i,i")) 0))]
  ""
  "@
  bclr %N1,%A0
  and %1,%0"
  [(set_attr "cc" "clobber,set_znv")])

(define_insn ""
  [(set (match_operand:QI 0 "memory_operand" "=R,T")
	(and:QI
	 (match_dup 0)
	 (not:QI (match_operand:QI 1 "nonmemory_operand" "i,d"))))]
  ""
  "@
  bclr %U1,%A0
  bclr %1,%0"
  [(set_attr "cc" "clobber,clobber")])

(define_insn ""
  [(set (match_operand:QI 0 "nonimmediate_operand" "+R,d")
	(subreg:QI
	  (ior:SI (subreg:SI (match_dup 0) 0)
		  (match_operand:SI 1 "const_int_operand" "i,i")) 0))]
  ""
  "@
  bset %U1,%A0
  or %1,%0"
  [(set_attr "cc" "clobber,set_znv")])

(define_expand "iorqi3"
  [(set (match_operand:QI 0 "nonimmediate_operand" "")
	(ior:QI (match_operand:QI 1 "nonimmediate_operand" "")
		(match_operand:QI 2 "nonmemory_operand" "")))]
  ""
  "")

(define_insn ""
  [(set (match_operand:QI 0 "nonimmediate_operand" "=R,T,r")
	(ior:QI (match_operand:QI 1 "nonimmediate_operand" "%0,0,0")
		;; This constraint should really be nonmemory_operand,
		;; but making it general_operand, along with the
		;; condition that not both input operands are MEMs, it
		;; here helps combine do a better job.
		(match_operand:QI 2 "general_operand" "i,d,ir")))]
  "TARGET_AM33 &&
   (GET_CODE (operands[2]) != MEM || GET_CODE (operands[1]) != MEM)"
  "@
  bset %U2,%A0
  bset %2,%0
  or %2,%0"
  [(set_attr "cc" "clobber,clobber,set_znv")])

(define_insn ""
  [(set (match_operand:QI 0 "nonimmediate_operand" "=R,T,d")
	(ior:QI (match_operand:QI 1 "nonimmediate_operand" "%0,0,0")
		;; This constraint should really be nonmemory_operand,
		;; but making it general_operand, along with the
		;; condition that not both input operands are MEMs, it
		;; here helps combine do a better job.
		(match_operand:QI 2 "general_operand" "i,d,id")))]
  "GET_CODE (operands[2]) != MEM || GET_CODE (operands[1]) != MEM"
  "@
  bset %U2,%A0
  bset %2,%0
  or %2,%0"
  [(set_attr "cc" "clobber,clobber,set_znv")])

(define_insn ""
  [(set (cc0)
     (compare (zero_extract:SI (match_operand:SI 0 "register_operand" "dx")
			       (match_operand 1 "const_int_operand" "")
			       (match_operand 2 "const_int_operand" ""))
	      (const_int 0)))]
  ""
  "*
{
  int len = INTVAL (operands[1]);
  int bit = INTVAL (operands[2]);
  int mask = 0;
  rtx xoperands[2];

  while (len > 0)
    {
      mask |= (1 << bit);
      bit++;
      len--;
    }

  xoperands[0] = operands[0];
  xoperands[1] = GEN_INT (trunc_int_for_mode (mask, SImode));
  output_asm_insn (\"btst %1,%0\", xoperands);
  return \"\";
}"
  [(set_attr "cc" "clobber")])

(define_insn ""
  [(set (cc0)
     (compare (zero_extract:SI (match_operand:QI 0 "general_operand" "R,dx")
			       (match_operand 1 "const_int_operand" "")
			       (match_operand 2 "const_int_operand" ""))
	      (const_int 0)))]
  "mask_ok_for_mem_btst (INTVAL (operands[1]), INTVAL (operands[2]))"
  "*
{
  int len = INTVAL (operands[1]);
  int bit = INTVAL (operands[2]);
  int mask = 0;
  rtx xoperands[2];

  while (len > 0)
    {
      mask |= (1 << bit);
      bit++;
      len--;
    }

  /* If the source operand is not a reg (i.e. it is memory), then extract the
     bits from mask that we actually want to test.  Note that the mask will
     never cross a byte boundary.  */
  if (!REG_P (operands[0]))
    {
      if (mask & 0xff)
	mask = mask & 0xff;
      else if (mask & 0xff00)
	mask = (mask >> 8) & 0xff;
      else if (mask & 0xff0000)
	mask = (mask >> 16) & 0xff;
      else if (mask & 0xff000000)
	mask = (mask >> 24) & 0xff;
    }

  xoperands[0] = operands[0];
  xoperands[1] = GEN_INT (trunc_int_for_mode (mask, SImode));
  if (GET_CODE (operands[0]) == REG)
    output_asm_insn (\"btst %1,%0\", xoperands);
  else
    output_asm_insn (\"btst %U1,%A0\", xoperands);
  return \"\";
}"
  [(set_attr "cc" "clobber")])

(define_insn ""
  [(set (cc0) (compare (and:SI (match_operand:SI 0 "register_operand" "dx")
			       (match_operand:SI 1 "const_int_operand" ""))
		       (const_int 0)))]
  ""
  "btst %1,%0"
  [(set_attr "cc" "clobber")])

(define_insn ""
  [(set (cc0)
     (compare (and:SI
	       (subreg:SI (match_operand:QI 0 "general_operand" "R,dx") 0)
	       (match_operand:SI 1 "const_8bit_operand" ""))
	      (const_int 0)))]
  ""
  "@
  btst %U1,%A0
  btst %1,%0"
  [(set_attr "cc" "clobber")])


;; ----------------------------------------------------------------------
;; JUMP INSTRUCTIONS
;; ----------------------------------------------------------------------

(define_expand "cbranchsi4"
  [(set (cc0)
	(compare (match_operand:SI 1 "register_operand" "")
		 (match_operand:SI 2 "nonmemory_operand" "")))
   (set (pc)
      (if_then_else
            (match_operator 0 "ordered_comparison_operator" [(cc0)
	                                                     (const_int 0)])
              (label_ref (match_operand 3 "" ""))
              (pc)))]
  ""
  "")

(define_expand "cbranchsf4"
  [(set (cc0)
	(compare (match_operand:SF 1 "register_operand" "")
		 (match_operand:SF 2 "nonmemory_operand" "")))
   (set (pc)
      (if_then_else
            (match_operator 0 "ordered_comparison_operator" [(cc0)
	                                                     (const_int 0)])
              (label_ref (match_operand 3 "" ""))
              (pc)))]
  "TARGET_AM33_2"
  "")


;; Conditional jump instructions

(define_insn ""
  [(set (pc)
	(if_then_else (match_operator 1 "comparison_operator"
				      [(cc0) (const_int 0)])
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "*
{
  if (cc_status.mdep.fpCC)
    return \"fb%b1 %0\";
  if ((cc_status.flags & CC_OVERFLOW_UNUSABLE) != 0
      && (GET_CODE (operands[1]) == GT
	  || GET_CODE (operands[1]) == GE
	  || GET_CODE (operands[1]) == LE
	  || GET_CODE (operands[1]) == LT))
    return 0;
  return \"b%b1 %0\";
}"
 [(set_attr "cc" "none")])

(define_insn ""
  [(set (pc)
	(if_then_else (match_operator 1 "comparison_operator"
				      [(cc0) (const_int 0)])
		      (pc)
		      (label_ref (match_operand 0 "" ""))))]
  ""
  "*
{
  if (cc_status.mdep.fpCC)
    return \"fb%B1 %0\";
  if ((cc_status.flags & CC_OVERFLOW_UNUSABLE) != 0
      && (GET_CODE (operands[1]) == GT
	  || GET_CODE (operands[1]) == GE
	  || GET_CODE (operands[1]) == LE
	  || GET_CODE (operands[1]) == LT))
    return 0;
  return \"b%B1 %0\";
}"
 [(set_attr "cc" "none")])

;; Unconditional and other jump instructions.

(define_insn "jump"
  [(set (pc)
	(label_ref (match_operand 0 "" "")))]
  ""
  "jmp %l0"
 [(set_attr "cc" "none")])

(define_insn "indirect_jump"
  [(set (pc) (match_operand:SI 0 "register_operand" "a"))]
  ""
  "jmp (%0)"
  [(set_attr "cc" "none")])

(define_expand "builtin_setjmp_receiver"
  [(match_operand 0 "" "")]
  "flag_pic"
  "
{
  if (flag_pic)
    emit_insn (gen_GOTaddr2picreg ());

  DONE;
}")

(define_expand "casesi"
  [(match_operand:SI 0 "register_operand" "")
   (match_operand:SI 1 "immediate_operand" "")
   (match_operand:SI 2 "immediate_operand" "")
   (match_operand 3 "" "") (match_operand 4 "" "")]
  ""
  "
{
  rtx table = gen_reg_rtx (SImode);
  rtx index = gen_reg_rtx (SImode);
  rtx addr = gen_reg_rtx (Pmode);
  rtx test;

  emit_move_insn (table, gen_rtx_LABEL_REF (VOIDmode, operands[3]));
  emit_move_insn (index, plus_constant (operands[0], - INTVAL (operands[1])));
  test = gen_rtx_fmt_ee (GTU, VOIDmode, index, operands[2]);
  emit_jump_insn (gen_cbranchsi4 (test, index, operands[2], operands[4]));
 
  emit_move_insn (index, gen_rtx_ASHIFT (SImode, index, const2_rtx));
  emit_move_insn (addr, gen_rtx_MEM (SImode,
				     gen_rtx_PLUS (SImode, table, index)));
  if (flag_pic)
    emit_move_insn (addr, gen_rtx_PLUS (SImode, addr, table));

  emit_jump_insn (gen_tablejump (addr, operands[3]));
  DONE;
}")

(define_insn "tablejump"
  [(set (pc) (match_operand:SI 0 "register_operand" "a"))
   (use (label_ref (match_operand 1 "" "")))]
  ""
  "jmp (%0)"
  [(set_attr "cc" "none")])

;; Call subroutine with no return value.

(define_expand "call"
  [(call (match_operand:QI 0 "general_operand" "")
	 (match_operand:SI 1 "general_operand" ""))]
  ""
  "
{
  if (flag_pic && GET_CODE (XEXP (operands[0], 0)) == SYMBOL_REF)
    {
      if (MN10300_GLOBAL_P (XEXP (operands[0], 0)))
	{
	  /* The PLT code won't run on AM30, but then, there's no
	     shared library support for AM30 either, so we just assume
	     the linker is going to adjust all @PLT relocs to the
	     actual symbols.  */
	  emit_use (pic_offset_table_rtx);
	  XEXP (operands[0], 0) = gen_sym2PLT (XEXP (operands[0], 0));
	}
      else
	XEXP (operands[0], 0) = gen_sym2PIC (XEXP (operands[0], 0));
    }
  if (! call_address_operand (XEXP (operands[0], 0), VOIDmode))
    XEXP (operands[0], 0) = force_reg (SImode, XEXP (operands[0], 0));
  emit_call_insn (gen_call_internal (XEXP (operands[0], 0), operands[1]));
  DONE;
}")

;; NB: Mode on match_operand 0 deliberately omitted in
;;     order to be able to match UNSPECs in PIC mode.
(define_insn "call_internal"
  [(call (mem:QI (match_operand 0 "call_address_operand" "aS"))
	 (match_operand:SI 1 "general_operand" "g"))]
  ""
  "*
{
  if (REG_P (operands[0]))
    return \"calls %C0\";
  else
    return \"call %C0,[],0\";
}"
  [(set_attr "cc" "clobber")])

;; Call subroutine, returning value in operand 0
;; (which must be a hard register).

(define_expand "call_value"
  [(set (match_operand 0 "" "")
	(call (match_operand:QI 1 "general_operand" "")
	      (match_operand:SI 2 "general_operand" "")))]
  ""
  "
{
  if (flag_pic && GET_CODE (XEXP (operands[1], 0)) == SYMBOL_REF)
    {
      if (MN10300_GLOBAL_P (XEXP (operands[1], 0)))
	{
	  /* The PLT code won't run on AM30, but then, there's no
	     shared library support for AM30 either, so we just assume
	     the linker is going to adjust all @PLT relocs to the
	     actual symbols.  */
	  emit_use (pic_offset_table_rtx);
	  XEXP (operands[1], 0) = gen_sym2PLT (XEXP (operands[1], 0));
	}
      else
	XEXP (operands[1], 0) = gen_sym2PIC (XEXP (operands[1], 0));
    }
  if (! call_address_operand (XEXP (operands[1], 0), VOIDmode))
    XEXP (operands[1], 0) = force_reg (SImode, XEXP (operands[1], 0));
  emit_call_insn (gen_call_value_internal (operands[0],
					   XEXP (operands[1], 0),
					   operands[2]));
  DONE;
}")

;; NB: Mode on match_operands 0 and 1 deliberately omitted
;;     in order to be able to match UNSPECs in PIC mode.
(define_insn "call_value_internal"
  [(set (match_operand               0 "register_operand" "=dax")
	(call (mem:QI (match_operand 1 "call_address_operand" "aS"))
	      (match_operand:SI      2 "general_operand" "g")))]
  ""
  "*
{
  if (REG_P (operands[1]))
    return \"calls %C1\";
  else
    return \"call %C1,[],0\";
}"
  [(set_attr "cc" "clobber")])

(define_expand "untyped_call"
  [(parallel [(call (match_operand 0 "" "")
                    (const_int 0))
              (match_operand 1 "" "")
              (match_operand 2 "" "")])]
  ""
  "
{
  int i;

  emit_call_insn (gen_call (operands[0], const0_rtx));

  for (i = 0; i < XVECLEN (operands[2], 0); i++)
    {
      rtx set = XVECEXP (operands[2], 0, i);
      emit_move_insn (SET_DEST (set), SET_SRC (set));
    }
  DONE;
}")

(define_insn "nop"
  [(const_int 0)]
  ""
  "nop"
  [(set_attr "cc" "none")])

;; ----------------------------------------------------------------------
;; EXTEND INSTRUCTIONS
;; ----------------------------------------------------------------------

(define_expand "zero_extendqisi2"
  [(set (match_operand:SI 0 "general_operand" "")
	(zero_extend:SI
	 (match_operand:QI 1 "general_operand" "")))]
  ""
  "")

(define_insn ""
  [(set (match_operand:SI 0 "nonimmediate_operand" "=dx,dx,dx,!dax,!dax,!dax")
	(zero_extend:SI
	 (match_operand:QI 1 "general_operand" "0,dax,m,0,dax,m")))]
  "TARGET_AM33"
  "@
  extbu %0
  mov %1,%0\;extbu %0
  movbu %1,%0
  extbu %0
  mov %1,%0\;extbu %0
  movbu %1,%0"
  [(set_attr "cc" "none_0hit")])

(define_insn ""
  [(set (match_operand:SI 0 "nonimmediate_operand" "=dx,dx,dx")
	(zero_extend:SI
	 (match_operand:QI 1 "general_operand" "0,d,m")))]
  ""
  "@
  extbu %0
  mov %1,%0\;extbu %0
  movbu %1,%0"
  [(set_attr "cc" "none_0hit")])

(define_expand "zero_extendhisi2"
  [(set (match_operand:SI 0 "general_operand" "")
	(zero_extend:SI
	 (match_operand:HI 1 "general_operand" "")))]
  ""
  "")

(define_insn ""
  [(set (match_operand:SI 0 "nonimmediate_operand" "=dx,dx,dx,!dax,!dax,!dax")
	(zero_extend:SI
	 (match_operand:HI 1 "general_operand" "0,dax,m,0,dax,m")))]
  "TARGET_AM33"
  "@
  exthu %0
  mov %1,%0\;exthu %0
  movhu %1,%0
  exthu %0
  mov %1,%0\;exthu %0
  movhu %1,%0"
  [(set_attr "cc" "none_0hit")])

(define_insn ""
  [(set (match_operand:SI 0 "nonimmediate_operand" "=dx,dx,dx")
	(zero_extend:SI
	 (match_operand:HI 1 "general_operand" "0,dx,m")))]
  ""
  "@
  exthu %0
  mov %1,%0\;exthu %0
  movhu %1,%0"
  [(set_attr "cc" "none_0hit")])

;;- sign extension instructions

(define_expand "extendqisi2"
  [(set (match_operand:SI 0 "general_operand" "")
	(sign_extend:SI
	 (match_operand:QI 1 "general_operand" "")))]
  ""
  "")

(define_insn ""
  [(set (match_operand:SI 0 "nonimmediate_operand" "=dx,dx,!dax,!dax")
	(sign_extend:SI
	 (match_operand:QI 1 "general_operand" "0,dx,0,dax")))]
  "TARGET_AM33"
  "@
  extb %0
  mov %1,%0\;extb %0
  extb %0
  mov %1,%0\;extb %0"
  [(set_attr "cc" "none_0hit")])

(define_insn ""
  [(set (match_operand:SI 0 "nonimmediate_operand" "=dx,dx")
	(sign_extend:SI
	 (match_operand:QI 1 "general_operand" "0,dx")))]
  ""
  "@
  extb %0
  mov %1,%0\;extb %0"
  [(set_attr "cc" "none_0hit")])

(define_expand "extendhisi2"
  [(set (match_operand:SI 0 "general_operand" "")
	(sign_extend:SI
	 (match_operand:HI 1 "general_operand" "")))]
  ""
  "")

(define_insn ""
  [(set (match_operand:SI 0 "nonimmediate_operand" "=dx,dx,!dax,!dax")
	(sign_extend:SI
	 (match_operand:HI 1 "general_operand" "0,dax,0,dax")))]
  "TARGET_AM33"
  "@
  exth %0
  mov %1,%0\;exth %0
  exth %0
  mov %1,%0\;exth %0"
  [(set_attr "cc" "none_0hit")])

(define_insn ""
  [(set (match_operand:SI 0 "nonimmediate_operand" "=dx,dx")
	(sign_extend:SI
	 (match_operand:HI 1 "general_operand" "0,dx")))]
  ""
  "@
  exth %0
  mov %1,%0\;exth %0"
  [(set_attr "cc" "none_0hit")])

;; ----------------------------------------------------------------------
;; SHIFTS
;; ----------------------------------------------------------------------

(define_expand "ashlsi3"
  [(set (match_operand:SI 0 "register_operand" "")
	(ashift:SI
	 (match_operand:SI 1 "register_operand" "")
	 (match_operand:QI 2 "nonmemory_operand" "")))]
  ""
  "")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=dax,dx,!dax")
	(ashift:SI
	 (match_operand:SI 1 "register_operand" "0,0,dax")
	 (match_operand:QI 2 "nonmemory_operand" "J,dxi,dax")))]
  "TARGET_AM33"
  "*
{
  if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 1)
    return \"add %0,%0\";

  if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 2)
    return \"asl2 %0\";

  if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 3
      && REGNO_REG_CLASS (true_regnum (operands[0])) == DATA_REGS)
    return \"asl2 %0\;add %0,%0\";

  if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 4
      && REGNO_REG_CLASS (true_regnum (operands[0])) == DATA_REGS)
    return \"asl2 %0\;asl2 %0\";

  if (true_regnum (operands[1]) == true_regnum (operands[0]))
    return \"asl %S2,%0\";

  if (REGNO_REG_CLASS (true_regnum (operands[0])) == DATA_REGS
      && REGNO_REG_CLASS (true_regnum (operands[1])) == DATA_REGS
      && true_regnum (operands[0]) != true_regnum (operands[2]))
    return \"mov %1,%0\;asl %S2,%0\";
  return \"asl %2,%1,%0\";
}"
  [(set_attr "cc" "set_zn")])

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=dax,dx,dx,dx,dx")
	(ashift:SI
	 (match_operand:SI 1 "register_operand" "0,0,0,0,0")
	 (match_operand:QI 2 "nonmemory_operand" "J,K,M,L,dxi")))]
  ""
  "@
  add %0,%0
  asl2 %0
  asl2 %0\;add %0,%0
  asl2 %0\;asl2 %0
  asl %S2,%0"
  [(set_attr "cc" "set_zn")])

(define_expand "lshrsi3"
  [(set (match_operand:SI 0 "register_operand" "")
	(lshiftrt:SI
	 (match_operand:SI 1 "register_operand" "")
	 (match_operand:QI 2 "nonmemory_operand" "")))]
  ""
  "")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=dx,!dax")
	(lshiftrt:SI
	 (match_operand:SI 1 "register_operand" "0,dax")
	 (match_operand:QI 2 "nonmemory_operand" "dxi,dax")))]
  "TARGET_AM33"
  "*
{
  if (true_regnum (operands[1]) == true_regnum (operands[0]))
    return \"lsr %S2,%0\";

  if (REGNO_REG_CLASS (true_regnum (operands[0])) == DATA_REGS
      && REGNO_REG_CLASS (true_regnum (operands[1])) == DATA_REGS
      && true_regnum (operands[0]) != true_regnum (operands[2]))
    return \"mov %1,%0\;lsr %S2,%0\";
  return \"lsr %2,%1,%0\";
}"
  [(set_attr "cc" "set_zn")])

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=dx")
	(lshiftrt:SI
	 (match_operand:SI 1 "register_operand" "0")
	 (match_operand:QI 2 "nonmemory_operand" "dxi")))]
  ""
  "lsr %S2,%0"
  [(set_attr "cc" "set_zn")])

(define_expand "ashrsi3"
  [(set (match_operand:SI 0 "register_operand" "")
	(ashiftrt:SI
	 (match_operand:SI 1 "register_operand" "")
	 (match_operand:QI 2 "nonmemory_operand" "")))]
  ""
  "")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=dx,!dax")
	(ashiftrt:SI
	 (match_operand:SI 1 "register_operand" "0,dax")
	 (match_operand:QI 2 "nonmemory_operand" "dxi,dax")))]
  "TARGET_AM33"
  "*
{
  if (true_regnum (operands[1]) == true_regnum (operands[0]))
    return \"asr %S2,%0\";

  if (REGNO_REG_CLASS (true_regnum (operands[0])) == DATA_REGS
      && REGNO_REG_CLASS (true_regnum (operands[1])) == DATA_REGS
      && true_regnum (operands[0]) != true_regnum (operands[2]))
    return \"mov %1,%0\;asr %S2,%0\";
  return \"asr %2,%1,%0\";
}"
  [(set_attr "cc" "set_zn")])

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=dx")
	(ashiftrt:SI
	 (match_operand:SI 1 "register_operand" "0")
	 (match_operand:QI 2 "nonmemory_operand" "dxi")))]
  ""
  "asr %S2,%0"
  [(set_attr "cc" "set_zn")])

;; ----------------------------------------------------------------------
;; FP INSTRUCTIONS
;; ----------------------------------------------------------------------
;;
;; The mn103 series does not have floating point instructions, but since
;; FP values are held in integer regs, we can clear the high bit easily
;; which gives us an efficient inline floating point absolute value.
;;
;; Similarly for negation of a FP value.
;;

(define_expand "absdf2"
  [(set (match_operand:DF 0 "register_operand" "")
        (abs:DF (match_operand:DF 1 "register_operand" "")))]
  ""
  "
{
  rtx target, result, insns;

  start_sequence ();
  target = operand_subword (operands[0], 1, 1, DFmode);
  result = expand_binop (SImode, and_optab,
			 operand_subword_force (operands[1], 1, DFmode),
			 GEN_INT (0x7fffffff), target, 0, OPTAB_WIDEN);

  gcc_assert (result);

  if (result != target)
    emit_move_insn (result, target);

  emit_move_insn (operand_subword (operands[0], 0, 1, DFmode),
		  operand_subword_force (operands[1], 0, DFmode));

  insns = get_insns ();
  end_sequence ();

  emit_insn (insns);
  DONE;
}")

(define_expand "abssf2"
  [(set (match_operand:SF 0 "register_operand" "")
        (abs:SF (match_operand:SF 1 "register_operand" "")))]
  ""
  "
{
  rtx result;
  rtx target;

  if (TARGET_AM33_2)
    {
      emit_insn (gen_abssf2_am33_2 (operands[0], operands[1]));
      DONE;
    }

  target = operand_subword_force (operands[0], 0, SFmode);
  result = expand_binop (SImode, and_optab,
			 operand_subword_force (operands[1], 0, SFmode),
			 GEN_INT (0x7fffffff), target, 0, OPTAB_WIDEN);
  gcc_assert (result);

  if (result != target)
    emit_move_insn (result, target);

  /* Make a place for REG_EQUAL.  */
  emit_move_insn (operands[0], operands[0]);
  DONE;
}")


(define_insn "abssf2_am33_2"
  [(set (match_operand:SF 0 "register_operand" "=f,f")
	(abs:SF (match_operand:SF 1 "register_operand" "0,?f")))]
  "TARGET_AM33_2"
  "@
   fabs %0
   fabs %1, %0"
  [(set_attr "cc" "none_0hit")])

(define_expand "negdf2"
  [(set (match_operand:DF 0 "register_operand" "")
        (neg:DF (match_operand:DF 1 "register_operand" "")))]
  ""
  "
{
  rtx target, result, insns;

  start_sequence ();
  target = operand_subword (operands[0], 1, 1, DFmode);
  result = expand_binop (SImode, xor_optab,
			 operand_subword_force (operands[1], 1, DFmode),
			 GEN_INT (trunc_int_for_mode (0x80000000, SImode)),
			 target, 0, OPTAB_WIDEN);

  gcc_assert (result);

  if (result != target)
    emit_move_insn (result, target);

  emit_move_insn (operand_subword (operands[0], 0, 1, DFmode),
		  operand_subword_force (operands[1], 0, DFmode));

  insns = get_insns ();
  end_sequence ();

  emit_insn (insns);
  DONE;
}")

(define_expand "negsf2"
  [(set (match_operand:SF 0 "register_operand" "")
        (neg:SF (match_operand:SF 1 "register_operand" "")))]
  ""
  "
{
  rtx result;
  rtx target;

  if (TARGET_AM33_2)
    {
      emit_insn (gen_negsf2_am33_2 (operands[0], operands[1]));
      DONE;
    }

  target = operand_subword_force (operands[0], 0, SFmode);
  result = expand_binop (SImode, xor_optab,
			 operand_subword_force (operands[1], 0, SFmode),
			 GEN_INT (trunc_int_for_mode (0x80000000, SImode)),
			 target, 0, OPTAB_WIDEN);
  gcc_assert (result);

  if (result != target)
    emit_move_insn (result, target);

  /* Make a place for REG_EQUAL.  */
  emit_move_insn (operands[0], operands[0]);
  DONE;
}")

(define_insn "negsf2_am33_2"
  [(set (match_operand:SF 0 "register_operand" "=f,f")
	(neg:SF (match_operand:SF 1 "register_operand" "0,?f")))]
  "TARGET_AM33_2"
  "@
   fneg %0
   fneg %1, %0"
  [(set_attr "cc" "none_0hit")])

(define_expand "sqrtsf2"
  [(set (match_operand:SF 0 "register_operand" "")
	(sqrt:SF (match_operand:SF 1 "register_operand" "")))]
  "TARGET_AM33_2 && flag_unsafe_math_optimizations"
  "
{
  rtx scratch = gen_reg_rtx (SFmode);
  emit_insn (gen_rsqrtsf2 (scratch, operands[1], CONST1_RTX (SFmode)));
  emit_insn (gen_divsf3 (operands[0], force_reg (SFmode, CONST1_RTX (SFmode)),
			 scratch));
  DONE;
}")

(define_insn "rsqrtsf2"
  [(set (match_operand:SF 0 "register_operand" "=f,f")
	(div:SF (match_operand:SF 2 "const_1f_operand" "F,F")
		(sqrt:SF (match_operand:SF 1 "register_operand" "0,?f"))))]
  "TARGET_AM33_2"
  "@
   frsqrt %0
   frsqrt %1, %0"
  [(set_attr "cc" "none_0hit")])

(define_insn "addsf3"
  [(set (match_operand:SF 0 "register_operand" "=f,f")
	(plus:SF (match_operand:SF 1 "register_operand" "%0,f")
		 (match_operand:SF 2 "general_operand" "f,?fF")))]
  "TARGET_AM33_2"
  "@
   fadd %2, %0
   fadd %2, %1, %0"
  [(set_attr "cc" "none_0hit")])

(define_insn "subsf3"
  [(set (match_operand:SF 0 "register_operand" "=f,f")
	(minus:SF (match_operand:SF 1 "register_operand" "0,f")
		  (match_operand:SF 2 "general_operand" "f,?fF")))]
  "TARGET_AM33_2"
  "@
   fsub %2, %0
   fsub %2, %1, %0"
  [(set_attr "cc" "none_0hit")])

(define_insn "mulsf3"
  [(set (match_operand:SF 0 "register_operand" "=f,f")
	(mult:SF (match_operand:SF 1 "register_operand" "%0,f")
		 (match_operand:SF 2 "general_operand" "f,?fF")))]
  "TARGET_AM33_2"
  "@
   fmul %2, %0
   fmul %2, %1, %0"
  [(set_attr "cc" "none_0hit")])

(define_insn "divsf3"
  [(set (match_operand:SF 0 "register_operand" "=f,f")
	(div:SF (match_operand:SF 1 "register_operand" "0,f")
		(match_operand:SF 2 "general_operand" "f,?fF")))]
  "TARGET_AM33_2"
  "@
   fdiv %2, %0
   fdiv %2, %1, %0"
  [(set_attr "cc" "none_0hit")])

(define_insn "fmaddsf4"
  [(set (match_operand:SF 0 "register_operand" "=A")
	(plus:SF (mult:SF (match_operand:SF 1 "register_operand" "%f")
			  (match_operand:SF 2 "register_operand" "f"))
		 (match_operand:SF 3 "register_operand" "f")))]
  "TARGET_AM33_2"
  "fmadd %1, %2, %3, %0"
  [(set_attr "cc" "none_0hit")])

(define_insn "fmsubsf4"
  [(set (match_operand:SF 0 "register_operand" "=A")
	(minus:SF (mult:SF (match_operand:SF 1 "register_operand" "%f")
			   (match_operand:SF 2 "register_operand" "f"))
		  (match_operand:SF 3 "register_operand" "f")))]
  "TARGET_AM33_2"
  "fmsub %1, %2, %3, %0"
  [(set_attr "cc" "none_0hit")])

(define_insn "fnmaddsf4"
  [(set (match_operand:SF 0 "register_operand" "=A")
	(minus:SF (match_operand:SF 3 "register_operand" "f")
		  (mult:SF (match_operand:SF 1 "register_operand" "%f")
			   (match_operand:SF 2 "register_operand" "f"))))]
  "TARGET_AM33_2"
  "fnmadd %1, %2, %3, %0"
  [(set_attr "cc" "none_0hit")])

(define_insn "fnmsubsf4"
  [(set (match_operand:SF 0 "register_operand" "=A")
	(minus:SF (neg:SF (mult:SF (match_operand:SF 1 "register_operand" "%f")
				   (match_operand:SF 2 "register_operand" "f")))
		  (match_operand:SF 3 "register_operand" "f")))]
  "TARGET_AM33_2"
  "fnmsub %1, %2, %3, %0"
  [(set_attr "cc" "none_0hit")])


;; ----------------------------------------------------------------------
;; PROLOGUE/EPILOGUE
;; ----------------------------------------------------------------------
(define_expand "prologue"
  [(const_int 0)]
  ""
  "expand_prologue (); DONE;")

(define_expand "epilogue"
  [(return)]
  ""
  "
{
  expand_epilogue ();
  DONE;
}")

(define_insn "return_internal"
  [(const_int 2)
   (return)]
  ""
  "rets"
  [(set_attr "cc" "clobber")])

;; This insn restores the callee saved registers and does a return, it
;; can also deallocate stack space.
(define_insn "return_internal_regs"
  [(const_int 0)
   (match_operand:SI 0  "const_int_operand" "i")
   (return)]
  ""
  "*
{
  fputs (\"\\tret \", asm_out_file);
  mn10300_print_reg_list (asm_out_file, mn10300_get_live_callee_saved_regs ());
  fprintf (asm_out_file, \",%d\\n\", (int) INTVAL (operands[0]));
  return \"\";
}"
  [(set_attr "cc" "clobber")])

;; This instruction matches one generated by mn10300_gen_multiple_store()
(define_insn "store_movm"
  [(match_parallel 0 "store_multiple_operation"
    [(set (reg:SI 9) (plus:SI (reg:SI 9) (match_operand 1 "" "")))])]
  ""
  "*
{
  fputs (\"\\tmovm \", asm_out_file);
  mn10300_print_reg_list (asm_out_file,
                          store_multiple_operation (operands[0], VOIDmode));
  fprintf (asm_out_file, \",(sp)\\n\");
  return \"\";
}"
  [(set_attr "cc" "clobber")])

(define_insn "return"
  [(return)]
  "can_use_return_insn ()"
  "*
{
  rtx next = next_active_insn (insn);

  if (next
      && GET_CODE (next) == JUMP_INSN
      && GET_CODE (PATTERN (next)) == RETURN)
    return \"\";
  else
    return \"rets\";
}"
  [(set_attr "cc" "clobber")])

;; Try to combine consecutive updates of the stack pointer (or any
;; other register for that matter).
(define_peephole
  [(set (match_operand:SI 0 "register_operand" "=dxay")
	(plus:SI (match_dup 0)
		 (match_operand 1 "const_int_operand" "")))
   (set (match_dup 0)
	(plus:SI (match_dup 0)
		 (match_operand 2 "const_int_operand" "")))]
  ""
  "*
{
  operands[1] = GEN_INT (INTVAL (operands[2]) + INTVAL (operands[1]));
  return \"add %1,%0\";
}"
  [(set_attr "cc" "clobber")])

;;
;; We had patterns to check eq/ne, but the they don't work because
;; 0x80000000 + 0x80000000 = 0x0 with a carry out.
;;
;; The Z flag and C flag would be set, and we have no way to
;; check for the Z flag set and C flag clear.
;;
;; This will work on the mn10200 because we can check the ZX flag
;; if the comparison is in HImode.
(define_peephole
  [(set (cc0) (compare (match_operand:SI 0 "register_operand" "dx")
		       (const_int 0)))
   (set (pc) (if_then_else (ge (cc0) (const_int 0))
			   (match_operand 1 "" "")
			   (pc)))]
  "dead_or_set_p (ins1, operands[0]) && REG_OK_FOR_INDEX_P (operands[0])"
  "add %0,%0\;bcc %1"
  [(set_attr "cc" "clobber")])

(define_peephole
  [(set (cc0) (compare (match_operand:SI 0 "register_operand" "dx")
		       (const_int 0)))
   (set (pc) (if_then_else (lt (cc0) (const_int 0))
			   (match_operand 1 "" "")
			   (pc)))]
  "dead_or_set_p (ins1, operands[0]) && REG_OK_FOR_INDEX_P (operands[0])"
  "add %0,%0\;bcs %1"
  [(set_attr "cc" "clobber")])

(define_peephole
  [(set (cc0) (compare (match_operand:SI 0 "register_operand" "dx")
		       (const_int 0)))
   (set (pc) (if_then_else (ge (cc0) (const_int 0))
			   (pc)
			   (match_operand 1 "" "")))]
  "dead_or_set_p (ins1, operands[0]) && REG_OK_FOR_INDEX_P (operands[0])"
  "add %0,%0\;bcs %1"
  [(set_attr "cc" "clobber")])

(define_peephole
  [(set (cc0) (compare (match_operand:SI 0 "register_operand" "dx")
		       (const_int 0)))
   (set (pc) (if_then_else (lt (cc0) (const_int 0))
			   (pc)
			   (match_operand 1 "" "")))]
  "dead_or_set_p (ins1, operands[0]) && REG_OK_FOR_INDEX_P (operands[0])"
  "add %0,%0\;bcc %1"
  [(set_attr "cc" "clobber")])

(define_expand "int_label"
  [(unspec [(match_operand:SI 0 "" "")] UNSPEC_INT_LABEL)]
  "" "")

(define_expand "GOTaddr2picreg"
  [(match_dup 0)]
  "" "
{
  /* It would be nice to be able to have int_label keep track of the
     counter and all, but if we add C code to it, we'll get an insn
     back, and we just want the pattern.  */
  operands[0] = gen_int_label (GEN_INT (mn10300_unspec_int_label_counter++));
  if (TARGET_AM33)
    emit_insn (gen_am33_loadPC (operands[0]));
  else
    emit_insn (gen_mn10300_loadPC (operands[0]));
  emit_insn (gen_add_GOT_to_pic_reg (copy_rtx (operands[0])));
  DONE;
}
")

(define_insn "am33_loadPC"
  [(parallel
    [(set (reg:SI PIC_REG) (pc))
     (use (match_operand 0 "" ""))])]
  "TARGET_AM33"
  "%0:\;mov pc,a2")


(define_insn_and_split "mn10300_loadPC"
  [(parallel
    [(set (reg:SI PIC_REG) (pc))
     (use (match_operand 0 "" ""))])]
  ""
  "#"
  "reload_completed"
  [(match_operand 0 "" "")]
  "
{
  rtx sp_reg = gen_rtx_REG (SImode, SP_REG);
  int need_stack_space = (get_frame_size () == 0
			  && crtl->outgoing_args_size == 0);

  if (need_stack_space)
    emit_move_insn (sp_reg, plus_constant (sp_reg, -4));

  emit_insn (gen_call_next_insn (operands[0]));

  if (need_stack_space)
    emit_insn (gen_pop_pic_reg ());
  else
    emit_move_insn (pic_offset_table_rtx, gen_rtx_MEM (SImode, sp_reg));

  DONE;
}")

(define_insn "call_next_insn"
  [(parallel
    [(set (mem:SI (reg:SI SP_REG)) (pc))
     (use (match_operand 0 "" ""))])]
  "reload_completed"
  "calls %0\;%0:")

(define_expand "add_GOT_to_pic_reg"
  [(set (reg:SI PIC_REG)
	(plus:SI
	 (reg:SI PIC_REG)
	 (const:SI
	  (unspec:SI [(match_operand:SI 0 "" "")] UNSPEC_GOTSYM_OFF))))]
  "")

(define_expand "symGOT2reg"
  [(match_operand:SI 0 "" "")
   (match_operand:SI 1 "" "")]
  ""
  "
{
  rtx insn = emit_insn (gen_symGOT2reg_i (operands[0], operands[1]));

  MEM_READONLY_P (SET_SRC (PATTERN (insn))) = 1;

  set_unique_reg_note (insn, REG_EQUAL, operands[1]);

  DONE;
}")

(define_expand "symGOT2reg_i"
  [(set (match_operand:SI 0 "" "")
	(mem:SI (plus:SI (reg:SI PIC_REG)
			 (const (unspec [(match_operand:SI 1 "" "")]
					UNSPEC_GOT)))))]
  ""
  "")

(define_expand "symGOTOFF2reg"
  [(match_operand:SI 0 "" "") (match_operand:SI 1 "" "")]
  ""
  "
{
  rtx insn = emit_insn (gen_symGOTOFF2reg_i (operands[0], operands[1]));

  set_unique_reg_note (insn, REG_EQUAL, operands[1]);

  DONE;
}")

(define_expand "symGOTOFF2reg_i"
  [(set (match_operand:SI 0 "" "")
	(const (unspec [(match_operand:SI 1 "" "")] UNSPEC_GOTOFF)))
  (set (match_dup 0) (plus:SI (match_dup 0) (reg:SI PIC_REG)))]
  ""
  "")

(define_expand "sym2PIC"
  [(unspec [(match_operand:SI 0 "" "")] UNSPEC_PIC)]
  "" "")

(define_expand "sym2PLT"
  [(unspec [(match_operand:SI 0 "" "")] UNSPEC_PLT)]
  "" "")
