;; GCC machine description for Matsushita MN10300
;; Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
;; 2005, 2006, 2007, 2008, 2009, 2010
;; 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.

(define_constants [
  (PIC_REG 6)
  (SP_REG  9)
  (CC_REG 51)

  (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")

;; Processor type.  This attribute must exactly match the processor_type
;; enumeration in mn10300.h.
(define_attr "cpu" "mn10300,am33,am33_2,am34"
  (const (symbol_ref "(enum attr_cpu) mn10300_tune_cpu")))


;; Pipeline description.

;; The AM33 only has a single pipeline.  It has five stages (fetch,
;; decode, execute, memory access, writeback) each of which normally
;; takes a single CPU clock cycle.

;; The timings attribute consists of two numbers, the first is the
;; throughput, which is the number of cycles the instruction takes
;; to execute and generate a result.  The second is the latency
;; which is the effective number of cycles the instruction takes to
;; execute if its result is used by the following instruction.  The
;; latency is always greater than or equal to the throughput.
;; These values were taken from the Appendix of the "MN103E Series
;; Instruction Manual" and the timings for the AM34.

;; Note - it would be nice to use strings rather than integers for
;; the possible values of this attribute, so that we can have the
;; gcc build mechanism check for values that are not supported by
;; the reservations below.  But this will not work because the code
;; in mn10300_adjust_sched_cost() needs integers not strings.

(define_attr "timings" "" (const_int 11))

(define_automaton "pipelining")
(define_cpu_unit "throughput" "pipelining")

(define_insn_reservation "throughput__1_latency__1"  1
  (eq_attr "timings" "11") "throughput")
(define_insn_reservation "throughput__1_latency__2"  2
  (eq_attr "timings" "12") "throughput,nothing")
(define_insn_reservation "throughput__1_latency__3"  3
  (eq_attr "timings" "13") "throughput,nothing*2")
(define_insn_reservation "throughput__1_latency__4"  4
  (eq_attr "timings" "14") "throughput,nothing*3")
(define_insn_reservation "throughput__2_latency__2"  2
  (eq_attr "timings" "22") "throughput*2")
(define_insn_reservation "throughput__2_latency__3"  3
  (eq_attr "timings" "23") "throughput*2,nothing")
(define_insn_reservation "throughput__2_latency__4"  4
  (eq_attr "timings" "24") "throughput*2,nothing*2")
(define_insn_reservation "throughput__2_latency__5"  5
  (eq_attr "timings" "25") "throughput*2,nothing*3")
(define_insn_reservation "throughput__3_latency__3"  3
  (eq_attr "timings" "33") "throughput*3")
(define_insn_reservation "throughput__3_latency__7"  7
  (eq_attr "timings" "37") "throughput*3,nothing*4")
(define_insn_reservation "throughput__4_latency__4"  4
  (eq_attr "timings" "44") "throughput*4")
(define_insn_reservation "throughput__4_latency__7"  7
  (eq_attr "timings" "47") "throughput*4,nothing*3")
(define_insn_reservation "throughput__4_latency__8"  8
  (eq_attr "timings" "48") "throughput*4,nothing*4")
(define_insn_reservation "throughput__5_latency__5"  5
  (eq_attr "timings" "55") "throughput*5")
(define_insn_reservation "throughput__6_latency__6"  6
  (eq_attr "timings" "66") "throughput*6")
(define_insn_reservation "throughput__7_latency__7"  7
  (eq_attr "timings" "77") "throughput*7")
(define_insn_reservation "throughput__7_latency__8"  8
  (eq_attr "timings" "78") "throughput*7,nothing")
(define_insn_reservation "throughput__8_latency__8"  8
  (eq_attr "timings" "88") "throughput*8")
(define_insn_reservation "throughput__9_latency__9"  9
  (eq_attr "timings" "99") "throughput*9")
(define_insn_reservation "throughput__8_latency_14" 14
  (eq_attr "timings" "814") "throughput*8,nothing*6")
(define_insn_reservation "throughput__9_latency_10" 10
  (eq_attr "timings" "910") "throughput*9,nothing")
(define_insn_reservation "throughput_10_latency_10" 10
  (eq_attr "timings" "1010") "throughput*10")
(define_insn_reservation "throughput_12_latency_16" 16
  (eq_attr "timings" "1216") "throughput*12,nothing*4")
(define_insn_reservation "throughput_13_latency_13" 13
  (eq_attr "timings" "1313") "throughput*13")
(define_insn_reservation "throughput_14_latency_14" 14
  (eq_attr "timings" "1414") "throughput*14")
(define_insn_reservation "throughput_13_latency_17" 17
  (eq_attr "timings" "1317") "throughput*13,nothing*4")
(define_insn_reservation "throughput_23_latency_27" 27
  (eq_attr "timings" "2327") "throughput*23,nothing*4")
(define_insn_reservation "throughput_25_latency_31" 31
  (eq_attr "timings" "2531") "throughput*25,nothing*6")
(define_insn_reservation "throughput_38_latency_39" 39
  (eq_attr "timings" "3839") "throughput*38,nothing")
(define_insn_reservation "throughput_39_latency_40" 40
  (eq_attr "timings" "3940") "throughput*39,nothing")
(define_insn_reservation "throughput_40_latency_40" 40
  (eq_attr "timings" "4040") "throughput*40")
(define_insn_reservation "throughput_41_latency_42" 42
  (eq_attr "timings" "4142") "throughput*41,nothing")
(define_insn_reservation "throughput_43_latency_44" 44
  (eq_attr "timings" "4344") "throughput*43,nothing")
(define_insn_reservation "throughput_45_latency_46" 46
  (eq_attr "timings" "4546") "throughput*45,nothing")
(define_insn_reservation "throughput_47_latency_53" 53
  (eq_attr "timings" "4753") "throughput*47,nothing*6")

;; Note - the conflict between memory load/store instructions
;; and floating point instructions described in section 1-7-4
;; of Chapter 3 of the MN103E Series Instruction Manual is
;; handled by the mn10300_adjust_sched_cost function.

;; ----------------------------------------------------------------------
;; MOVE INSTRUCTIONS
;; ----------------------------------------------------------------------

;; movqi

(define_expand "movqi"
  [(set (match_operand:QI 0 "nonimmediate_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 "*am33_movqi"
  [(set (match_operand:QI 0 "nonimmediate_operand"
			  ;; 0       1      2      3     4       5
			  "=d*x*a*f, d*x*a, d*x*a, m,   *f,      d*x*a")
	(match_operand:QI 1 "general_operand"
			   "0,       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:
        gcc_assert (! CONST_DOUBLE_P (operands[1]));

        if (REGNO_REG_CLASS (true_regnum (operands[0])) == EXTENDED_REGS
	    && CONST_INT_P (operands[1]))
	  {
	    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 2:
      case 3:
        return \"movbu %1,%0\";
      case 4:
      case 5:
        return \"fmov %1,%0\";
      default:
        gcc_unreachable ();
      }
  }"
  [(set_attr_alternative "timings"
			 [(const_int 11)
			  (if_then_else (eq_attr "cpu" "am34")
					(const_int 11) (const_int 22))
			  (if_then_else (eq_attr "cpu" "am34")
					(const_int 13) (const_int 24))
			  (if_then_else (eq_attr "cpu" "am34")
					(const_int 13) (const_int 24))
			  (if_then_else (eq_attr "cpu" "am34")
					(const_int 47) (const_int 25))
			  (if_then_else (eq_attr "cpu" "am34")
					(const_int 47) (const_int 25))
			 ])
  ]
)

(define_insn "*mn10300_movqi"
  [(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:
    case 2:
    case 3:
    case 4:
      gcc_assert (! CONST_DOUBLE_P (operands[1]));
      return \"mov %1,%0\";
    case 5:
    case 6:
      return \"movbu %1,%0\";
    default:
      gcc_unreachable ();
    }
}"
  [(set_attr_alternative "timings"
			 [(const_int 11)
			  (const_int 11)
			  (if_then_else (eq_attr "cpu" "am34")
			  		(const_int 11) (const_int 22))
			  (if_then_else (eq_attr "cpu" "am34")
			  		(const_int 11) (const_int 22))
			  (if_then_else (eq_attr "cpu" "am34")
			  		(const_int 11) (const_int 22))
			  (if_then_else (eq_attr "cpu" "am34")
			  		(const_int 13) (const_int 24))
			  (if_then_else (eq_attr "cpu" "am34")
			  		(const_int 13) (const_int 24))
			 ])
  ]
)

;; movhi

(define_expand "movhi"
  [(set (match_operand:HI 0 "nonimmediate_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 "*am33_movhi"
  [(set (match_operand:HI 0 "nonimmediate_operand"
			  ;; 0       1       2      3     4         5
			  "=d*x*a*f, d*x*a,  d*x*a, m,    *f,       d*x*a")
	(match_operand:HI 1 "general_operand"
			  "0,        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:
      gcc_assert (! CONST_DOUBLE_P (operands[1]));

      if (REGNO_REG_CLASS (true_regnum (operands[0])) == EXTENDED_REGS
	  && CONST_INT_P (operands[1]))
	{
	  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 2:
    case 3:
      return \"movhu %1,%0\";
    case 4:
    case 5:
      return \"fmov %1,%0\";
    default:
      gcc_unreachable ();
    }
}"
  [(set_attr_alternative "timings"
			 [(const_int 11)
			  (if_then_else (eq_attr "cpu" "am34")
			  		(const_int 11) (const_int 22))
			  (if_then_else (eq_attr "cpu" "am34")
			  		(const_int 13) (const_int 24))
			  (if_then_else (eq_attr "cpu" "am34")
			  		(const_int 13) (const_int 24))
			  (if_then_else (eq_attr "cpu" "am34")
			  		(const_int 47) (const_int 25))
			  (if_then_else (eq_attr "cpu" "am34")
			  		(const_int 47) (const_int 25))
			 ])
  ]
)

(define_insn "*mn10300_movhi"
  [(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:
    case 2:
    case 3:
    case 4:
      gcc_assert (! CONST_DOUBLE_P (operands[1]));
      return \"mov %1,%0\";
    case 5:
    case 6:
      return \"movhu %1,%0\";
    default:
      gcc_unreachable ();
    }
}"
  [(set_attr_alternative "timings"
			 [(const_int 11)
			  (const_int 11)
			  (if_then_else (eq_attr "cpu" "am34")
			  		(const_int 11) (const_int 22))
			  (if_then_else (eq_attr "cpu" "am34")
			  		(const_int 11) (const_int 22))
			  (if_then_else (eq_attr "cpu" "am34")
			  		(const_int 11) (const_int 22))
			  (if_then_else (eq_attr "cpu" "am34")
			  		(const_int 13) (const_int 24))
			  (if_then_else (eq_attr "cpu" "am34")
			  		(const_int 13) (const_int 24))
			 ])
  ]
)

;; 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"))]
  ""
  "
{
  gcc_assert (REGNO (operands[0]) != REGNO (operands[2]));

  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]"
  [(set (attr "timings") (if_then_else (eq_attr "cpu" "am34")
				       (const_int 44) (const_int 33)))]
)

(define_expand "movsi"
  [(set (match_operand:SI 0 "nonimmediate_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 (MEM_P (operands[0]))
	    operands[1] = force_reg (Pmode, operands[1]);
	  else
	    {
	      temp = (!can_create_pseudo_p ()
		      ? operands[0]
		      : gen_reg_rtx (Pmode));
	      operands[1] = mn10300_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 = mn10300_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 "*movsi_internal"
  [(set (match_operand:SI 0 "nonimmediate_operand"
			  "=dax, dax,  m,   dax, axR, !*y")
	(match_operand:SI 1 "general_operand"
			  "0,    Idax, dax, im,  !*y, axR"))
  ]
  "register_operand (operands[0], SImode)
   || register_operand (operands[1], SImode)"
  "*
  {
    if (which_alternative == 0)
      return \"nop\";

    gcc_assert (! CONST_DOUBLE_P (operands[1]));

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

        if (((val & 0x80) && ! (val & 0xffffff00))
            || ((val & 0x800000) && ! (val & 0xff000000)))
          return \"movu %1, %0\";
      }

    return \"mov %1, %0\";
  }"
  [(set_attr_alternative "timings"
			 [(const_int 11)
			  (if_then_else (eq_attr "cpu" "am34")
					(const_int 13) (const_int 24))
			  (if_then_else (eq_attr "cpu" "am34")
					(const_int 13) (const_int 24))
			  (if_then_else (eq_attr "cpu" "am34")
					(const_int 13) (const_int 24))
			  (if_then_else (eq_attr "cpu" "am34")
					(const_int 13) (const_int 24))
			  (if_then_else (eq_attr "cpu" "am34")
					(const_int 13) (const_int 24))
			 ])
  ]
)

(define_expand "movsf"
  [(set (match_operand:SF 0 "nonimmediate_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 "*movsf_internal"
  [(set (match_operand:SF 0 "nonimmediate_operand"
			  ;; 0    1    2       3     4     5
			  "=fdxa, dxa, f,      dxaQ, daxm, dax")
	(match_operand:SF 1 "general_operand"
			  " 0,    G,   fdxaQF, f,    dax,  daxFm"))
  ]
  "register_operand (operands[0], SFmode)
   || register_operand (operands[1], SFmode)"
  "*
  {
    switch (which_alternative)
      {
      case 0:
        return \"nop\";
      /* case 1: below.  */
      case 2:
      case 3:
        return \"fmov %1, %0\";
      case 1:
      case 4:
      case 5:
        if (REGNO_REG_CLASS (true_regnum (operands[0])) == EXTENDED_REGS
	    && CONST_INT_P (operands[1]))
	  {
	    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_alternative "timings"
			 [(const_int 11)
			  (if_then_else (eq_attr "cpu" "am34")
					(const_int 13) (const_int 24))
			  (if_then_else (eq_attr "cpu" "am34")
					(const_int 47) (const_int 25))
			  (if_then_else (eq_attr "cpu" "am34")
					(const_int 47) (const_int 25))
			  (if_then_else (eq_attr "cpu" "am34")
					(const_int 13) (const_int 24))
			  (if_then_else (eq_attr "cpu" "am34")
					(const_int 13) (const_int 24))
			 ])
  ]
)

(define_expand "movdi"
  [(set (match_operand:DI 0 "nonimmediate_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 "*movdi_internal"                   ;;   0 1  2  3 4   5   6  7 8  9
  [(set (match_operand:DI 0 "nonimmediate_operand" "=dx,ax,dx,a,dxm,dxm,a, a,dx,a")
	(match_operand:DI 1 "general_operand"        "0,0, I, I,dx, a,  dx,a,im,im"))]
  "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 \"mov 0, %L0\;mov 0, %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:
	if (CONST_INT_P (operands[1]))
	  {
	    rtx low, high;
	    split_double (operands[1], &low, &high);
	    val[0] = INTVAL (low);
	    val[1] = INTVAL (high);
	  }
	if (CONST_DOUBLE_P (operands[1]))
	  {
	    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 (MEM_P (operands[1])
	    && 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 (REG_P (temp));

	    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 (MEM_P (operands[1])
		 && 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 ((CONST_INT_P (operands[1])
		 || CONST_DOUBLE_P (operands[1]))
		&& val[0] == 0)
	      {
		if (REGNO_REG_CLASS (REGNO (operands[0])) == DATA_REGS)
		  output_asm_insn (\"mov 0, %L0\", operands);
		else
		  output_asm_insn (\"mov %L1,%L0\", operands);
	      }
	    else if ((CONST_INT_P (operands[1])
		      || CONST_DOUBLE_P (operands[1]))
		     && (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 ((CONST_INT_P (operands[1])
		 || CONST_DOUBLE_P (operands[1]))
		&& val[1] == 0)
	      {
		if (REGNO_REG_CLASS (REGNO (operands[0])) == DATA_REGS)
		  output_asm_insn (\"mov 0, %H0\", operands);
		else
		  output_asm_insn (\"mov %H1,%H0\", operands);
	      }
	    else if ((CONST_INT_P (operands[1])
		      || CONST_DOUBLE_P (operands[1]))
		     && val[0] == val[1])
	      output_asm_insn (\"mov %L0,%H0\", operands);
	    else if ((CONST_INT_P (operands[1])
		      || CONST_DOUBLE_P (operands[1]))
		     && (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 ();
    }
  }"
  ;; The timing of "37" is an approximation of the worst case sceanario.
  [(set_attr_alternative "timings"
			 [(const_int 11)
			  (const_int 11)
			  (const_int 22)
			  (const_int 22)
			  (const_int 37)
			  (const_int 37)
			  (const_int 37)
			  (const_int 37)
			  (const_int 37)
			  (const_int 37)
			 ])
  ]
)

(define_expand "movdf"
  [(set (match_operand:DF 0 "nonimmediate_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 "*am33_2_movdf"
  [(set (match_operand:DF 0 "nonimmediate_operand"
			  ;; 0   1   2    3    4 5 6   7   8  9 10 11
			  "=fdax,dax,fdxa,f,   f,Q,dxm,dxm,a, a,dx,a")
	(match_operand:DF 1 "general_operand"
			  " 0,   G,  f,   dxaF,Q,f,dx, a,  dx,a,Fm,Fm"))]
  "TARGET_AM33_2
   && (register_operand (operands[0], DFmode)
       || register_operand (operands[1], DFmode))"
  "*
  {
    long val[2];
    REAL_VALUE_TYPE rv;

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

      case 1:
	return \"mov 0, %L0\; mov 0, %H0\";

      case 2:
      case 3:
        return \"fmov %L1, %L0\; fmov %H1, %H0\";

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

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

      case 6:
      case 7:
      case 8:
      case 9:
      case 10:
      case 11:
	if (CONST_INT_P (operands[1]))
	  {
	    rtx low, high;
	    split_double (operands[1], &low, &high);
	    val[0] = INTVAL (low);
	    val[1] = INTVAL (high);
	  }
	if (CONST_DOUBLE_P (operands[1]))
	  {
	    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 (MEM_P (operands[1])
	    && 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 (REG_P (temp));

	    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 (MEM_P (operands[1])
		 && 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 ((CONST_INT_P (operands[1])
		 || CONST_DOUBLE_P (operands[1]))
		&& val[0] == 0)
	      {
		if (REGNO_REG_CLASS (REGNO (operands[0])) == DATA_REGS)
		  output_asm_insn (\"mov 0, %L0\", operands);
		else
		  output_asm_insn (\"mov %L1,%L0\", operands);
	      }
	    else if ((CONST_INT_P (operands[1])
		      || CONST_DOUBLE_P (operands[1]))
		     && (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 ((CONST_INT_P (operands[1])
		 || CONST_DOUBLE_P (operands[1]))
		&& val[1] == 0)
	      {
		if (REGNO_REG_CLASS (REGNO (operands[0])) == DATA_REGS)
		  output_asm_insn (\"mov 0, %H0\", operands);
		else
		  output_asm_insn (\"mov %H1, %H0\", operands);
	      }
	    else if ((CONST_INT_P (operands[1])
		      || CONST_DOUBLE_P (operands[1]))
		     && val[0] == val[1])
	      output_asm_insn (\"mov %L0,%H0\", operands);
	    else if ((CONST_INT_P (operands[1])
		      || CONST_DOUBLE_P (operands[1]))
		     && (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 ();
    }
  }"
  ;; The timing of "37" is an approximation of the worst case sceanario.
  [(set_attr_alternative "timings"
			 [(const_int 11)
			  (const_int 22)
			  (const_int 22)
			  (const_int 22)
			  (const_int 22)
			  (const_int 37)
			  (const_int 37)
			  (const_int 37)
			  (const_int 37)
			  (const_int 37)
			  (const_int 37)
			  (const_int 37)
			 ])
  ]
)

(define_insn "*mn10300_movdf"
  [(set (match_operand:DF 0 "nonimmediate_operand"
			  ;;0    1    2    3    4   5  6   7
			  "=dxa, dax, dxm, dxm, a,  a, dx, a")
	(match_operand:DF 1 "general_operand"
			  " 0,   G,   dx,  a,   dx, a, Fm, Fm"))]
  "register_operand (operands[0], DFmode)
   || register_operand (operands[1], DFmode)"
  "*
  {
    long val[2];
    REAL_VALUE_TYPE rv;

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

      case 1:
	return \"mov 0, %L0\; mov 0, %H0\";

      case 2:
      case 3:
      case 4:
      case 5:
      case 6:
      case 7:
	if (CONST_INT_P (operands[1]))
	  {
	    rtx low, high;
	    split_double (operands[1], &low, &high);
	    val[0] = INTVAL (low);
	    val[1] = INTVAL (high);
	  }
        if (CONST_DOUBLE_P (operands[1]))
	  {
	    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 (MEM_P (operands[1])
	    && 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 (REG_P (temp));

	    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 (MEM_P (operands[1])
		 && 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 ((CONST_INT_P (operands[1])
		 || CONST_DOUBLE_P (operands[1]))
		&& val[0] == 0)
	      {
		if (REGNO_REG_CLASS (REGNO (operands[0])) == DATA_REGS)
		  output_asm_insn (\"mov 0, %L0\", operands);
		else
		  output_asm_insn (\"mov %L1, %L0\", operands);
	      }
	    else if ((CONST_INT_P (operands[1])
		      || CONST_DOUBLE_P (operands[1]))
		     && (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 ((CONST_INT_P (operands[1])
		 || CONST_DOUBLE_P (operands[1]))
		&& val[1] == 0)
	      {
		if (REGNO_REG_CLASS (REGNO (operands[0])) == DATA_REGS)
		  output_asm_insn (\"mov 0, %H0\", operands);
		else
		  output_asm_insn (\"mov %H1, %H0\", operands);
	      }
	    else if ((CONST_INT_P (operands[1])
		      || CONST_DOUBLE_P (operands[1]))
		     && val[0] == val[1])
	      output_asm_insn (\"mov %L0, %H0\", operands);
	    else if ((CONST_INT_P (operands[1])
		      || CONST_DOUBLE_P (operands[1]))
		     && (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 ();
    }
  }"
  ;; Timings of "37" is approximation of the worst case sceanario.
  [(set_attr_alternative "timings"
			 [(const_int 11)
			  (const_int 22)
			  (const_int 37)
			  (const_int 37)
			  (const_int 37)
			  (const_int 37)
			  (const_int 37)
			  (const_int 37)
			 ])
  ]
)

;; ----------------------------------------------------------------------
;; ADD INSTRUCTIONS
;; ----------------------------------------------------------------------

(define_expand "addsi3"
  [(parallel [(set (match_operand:SI          0 "register_operand")
		   (plus:SI (match_operand:SI 1 "register_operand")
			    (match_operand:SI 2 "nonmemory_operand")))
	      (clobber (reg:CC CC_REG))
	     ])
  ]
  ""
  "")

(define_insn "*am33_addsi3"
  [(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")))
   (clobber (reg:CC CC_REG))
  ]
  "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 "timings" "11,11,11,11,11,11,22")]
)

(define_insn "*mn10300_addsi3"
  [(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")))
   (clobber (reg:CC CC_REG))
  ]
  ""
  "*
{
  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 "timings" "11,11,11,11,11,22")]
)

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

(define_expand "subsi3"
  [(parallel [(set (match_operand:SI           0 "register_operand")
		   (minus:SI (match_operand:SI 1 "register_operand")
			     (match_operand:SI 2 "nonmemory_operand")))
	      (clobber (reg:CC CC_REG))
	     ])
  ]
  ""
  "")

(define_insn "*am33_subsi3"
  [(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")))
   (clobber (reg:CC CC_REG))
  ]
  "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 "timings" "11,22")]
)

(define_insn "*mn10300_subsi3"
  [(set (match_operand:SI           0 "register_operand" "=dax")
	(minus:SI (match_operand:SI 1 "register_operand" "0")
		  (match_operand:SI 2 "nonmemory_operand" "daxi")))
   (clobber (reg:CC CC_REG))
  ]
  ""
  "sub %2,%0"
  [(set (attr "timings") (if_then_else (eq_attr "cpu" "am34")
				       (const_int 11) (const_int 22)))]
)

(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"))))
   (clobber (reg:CC CC_REG))
  ]
  "TARGET_AM33"
  "mul %1,%2,%H0,%L0"
  [(set (attr "timings") (if_then_else (eq_attr "cpu" "am34")
				       (const_int 24) (const_int 23)))]
)

(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"))))
   (clobber (reg:CC CC_REG))
  ]
  "TARGET_AM33"
  "mulu %1,%2,%H0,%L0"
  [(set (attr "timings") (if_then_else (eq_attr "cpu" "am34")
				       (const_int 24) (const_int 23)))]
)

(define_expand "mulsi3"
  [(parallel [(set (match_operand:SI          0 "register_operand")
		   (mult:SI (match_operand:SI 1 "register_operand")
			    (match_operand:SI 2 "register_operand")))
	      (clobber (reg:CC CC_REG))
	     ])
  ]
  ""
  "")

(define_insn "*am33_mulsi3"
  [(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")))
   (clobber (reg:CC CC_REG))
  ]
  "TARGET_AM33"
  "*
{
  if (TARGET_MULT_BUG)
    return \"nop\;nop\;mul %2,%0\";
  else
    return \"mul %2,%0\";
}"
  [(set (attr "timings") (if_then_else (eq_attr "cpu" "am34") (const_int 24) (const_int 23)))]
)

(define_insn "*mn10300_mulsi3"
  [(set (match_operand:SI          0 "register_operand" "=dx")
	(mult:SI (match_operand:SI 1 "register_operand" "%0")
		 (match_operand:SI 2 "register_operand" "dx")))
   (clobber (reg:CC CC_REG))
  ]
  ""
  "*
{
  if (TARGET_MULT_BUG)
    return \"nop\;nop\;mul %2,%0\";
  else
    return \"mul %2,%0\";
}"
  [(set (attr "timings") (if_then_else (eq_attr "cpu" "am34")
  	      		 	       (const_int 24) (const_int 23)))]
)

(define_expand "udivmodsi4"
  [(parallel [(set (match_operand:SI          0 "register_operand")
		   (udiv:SI (match_operand:SI 1 "general_operand")
			    (match_operand:SI 2 "general_operand")))
	      (set (match_operand:SI          3 "register_operand")
		   (umod:SI (match_dup 1) (match_dup 2)))
	      (clobber (reg:CC CC_REG))
	     ])
  ]
  ""
  "{
    if (!register_operand (operands[1], SImode))
      operands[1] = copy_to_mode_reg (SImode, operands[1]);
    if (!register_operand (operands[2], SImode))
      operands[2] = copy_to_mode_reg (SImode, operands[2]);
   }"
)

(define_insn "*udivmodsi4"
  [(set (match_operand:SI          0 "register_operand" "=dx")
	(udiv:SI (match_operand:SI 1 "register_operand" "0")
		 (match_operand:SI 2 "register_operand" "dx")))
   (set (match_operand:SI          3 "register_operand" "=&d")
	(umod:SI (match_dup 1) (match_dup 2)))
   (clobber (reg:CC CC_REG))
  ]
  ""
  "*
{
  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\";
}"
  ;; Timings:  AM33   AM34
  ;;  SUB       1/1    1/1
  ;;  MOV       1/1    1/1
  ;;  DIVU     38/39  42/43
  ;;  MOV       1/1    1/1
  ;;  --------------------
  ;;  total    41/42  45/46  (worst case sceanario)
  [(set (attr "timings") (if_then_else (eq_attr "cpu" "am34")
  	      		 	       (const_int 4546) (const_int 4142)))]
)

(define_insn "divmodsi4"
  [(set (match_operand:SI          0 "register_operand" "=dx")
	(div:SI (match_operand:SI  1 "register_operand"  "0")
		 (match_operand:SI 2 "register_operand"  "dx")))
   (set (match_operand:SI          3 "register_operand" "=d")
	(mod:SI (match_dup 1) (match_dup 2)))
   (clobber (reg:CC CC_REG))
  ]
  ""
  "*
{
  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\";
}"
  ;; Timings:  AM33   AM34
  ;;  EXT       1/1    1/1
  ;;  DIV      38/39  42/43
  ;;  --------------------
  ;;  total    39/40  43/44  (worst case sceanario)
  [(set (attr "timings") (if_then_else (eq_attr "cpu" "am34")
  	      		 	       (const_int 4344) (const_int 3940)))]
)

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

(define_expand "andsi3"
  [(parallel [(set (match_operand:SI         0 "register_operand")
		   (and:SI (match_operand:SI 1 "register_operand")
			   (match_operand:SI 2 "nonmemory_operand")))
	      (clobber (reg:CC CC_REG))
	     ])
  ]
  ""
  "")

(define_insn "*am33_andsi3"
  [(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")))
   (clobber (reg:CC CC_REG))
  ]
  "TARGET_AM33"
  {
    if (CONST_INT_P (operands[2]))
      switch (INTVAL (operands[2]))
        {
        case 0xff:       return "extbu %0";
        case 0xffff:     return "exthu %0";
        case 0x7fffffff: return "add  %0, %0; lsr 1, %0";
        case 0x3fffffff: return "asl2 %0; lsr 2, %0";
        case 0x1fffffff: return "add  %0, %0; asl2 %0; lsr 3, %0";
        case 0x0fffffff: return "asl2 %0; asl2 %0; lsr 4, %0";
        case 0xfffffffe: return "lsr 1, %0; add  %0, %0";
        case 0xfffffffc: return "lsr 2, %0; asl2 %0";
        case 0xfffffff8: return "lsr 3, %0; add  %0, %0; asl2 %0";
        case 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 "timings" "33")]
)

(define_insn "*mn10300_andsi3"
  [(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")))
   (clobber (reg:CC CC_REG))
  ]
  ""
  {
    if (CONST_INT_P (operands[2]))
      switch (INTVAL (operands[2]))
        {
        case 0xff:       return "extbu %0";
        case 0xffff:     return "exthu %0";
        case 0x7fffffff: return "add  %0, %0; lsr 1, %0";
        case 0x3fffffff: return "asl2 %0; lsr 2, %0";
        case 0x1fffffff: return "add  %0, %0; asl2 %0; lsr 3, %0";
        case 0x0fffffff: return "asl2 %0; asl2 %0; lsr 4, %0";
        case 0xfffffffe: return "lsr 1, %0; add  %0, %0";
        case 0xfffffffc: return "lsr 2, %0; asl2 %0";
        case 0xfffffff8: return "lsr 3, %0; add  %0, %0; asl2 %0";
        case 0xfffffff0: return "lsr 4, %0; asl2 %0; asl2 %0";
	}

    return "and %2, %0";
  }
  [(set_attr "timings" "33")]
)

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

(define_expand "iorsi3"
  [(parallel [(set (match_operand:SI         0 "register_operand")
		   (ior:SI (match_operand:SI 1 "register_operand")
			   (match_operand:SI 2 "nonmemory_operand")))
	      (clobber (reg:CC CC_REG))
	     ])
  ]
  ""
  "")

(define_insn "*am33_iorsi3"
  [(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")))
   (clobber (reg:CC CC_REG))
  ]
  "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 "timings" "22")]
)

(define_insn "*mn10300_iorsi3"
  [(set (match_operand:SI         0 "register_operand" "=dx")
	(ior:SI (match_operand:SI 1 "register_operand" "%0")
		(match_operand:SI 2 "nonmemory_operand" "dxi")))
   (clobber (reg:CC CC_REG))
  ]
  ""
  "or %2,%0"
  [(set_attr "timings" "33")]
)

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

(define_expand "xorsi3"
  [(parallel [(set (match_operand:SI         0 "register_operand")
		   (xor:SI (match_operand:SI 1 "register_operand")
			   (match_operand:SI 2 "nonmemory_operand")))
	      (clobber (reg:CC CC_REG))
	     ])
  ]
  ""
  "")

(define_insn "*am33_xorsi3"
  [(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")))
   (clobber (reg:CC CC_REG))
  ]
  "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 "timings" "22")]
)

(define_insn "*mn10300_xorsi3"
  [(set (match_operand:SI         0 "register_operand" "=dx")
	(xor:SI (match_operand:SI 1 "register_operand" "%0")
		(match_operand:SI 2 "nonmemory_operand" "dxi")))
   (clobber (reg:CC CC_REG))
  ]
  ""
  "xor %2,%0"
  [(set_attr "timings" "11")]
)

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

(define_expand "one_cmplsi2"
  [(parallel [(set (match_operand:SI         0 "register_operand")
		   (not:SI (match_operand:SI 1 "register_operand")))
	      (clobber (reg:CC CC_REG))
	     ])
  ]
  ""
  "")

(define_insn "*am33_cmplsi2"
  [(set (match_operand:SI         0 "register_operand" "=dx,!dax")
	(not:SI (match_operand:SI 1 "register_operand" "0,0")))
   (clobber (reg:CC CC_REG))
  ]
  "TARGET_AM33"
  "not %0"
)

(define_insn "*mn10300_cmplsi2"
  [(set (match_operand:SI         0 "register_operand" "=dx")
	(not:SI (match_operand:SI 1 "register_operand" "0")))
   (clobber (reg:CC CC_REG))
  ]
  ""
  "not %0"
)

;; -----------------------------------------------------------------
;; 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 "*byte_clear"
  [(set (match_operand:QI 0 "nonimmediate_operand" "=R,d") (const_int 0))
   (clobber (reg:CC CC_REG))
   ]
  "(! MEM_P (operands[0])) || (! MEM_VOLATILE_P (operands[0])
                               && GET_CODE (XEXP (operands[0], 0)) != PLUS)"
  "@
  bclr 255,%A0
  clr %0"
  [(set_attr_alternative "timings"
			 [(if_then_else (eq_attr "cpu" "am34")
					(const_int 66) (const_int 77))
			  (const_int 11)
			 ])
  ]
)

(define_insn "*byte_set"
  [(set (match_operand:QI 0 "nonimmediate_operand" "=R,d") (const_int -1))
   (clobber (reg:CC CC_REG))
  ]
  "(! MEM_P (operands[0])) || (! MEM_VOLATILE_P (operands[0])
                               && GET_CODE (XEXP (operands[0], 0)) != PLUS)"
  "@
  bset 255,%A0
  mov -1,%0"
  [(set_attr_alternative "timings"
			 [(if_then_else (eq_attr "cpu" "am34")
					(const_int 66) (const_int 77))
			  (const_int 11)
			 ])
  ]
)

(define_insn "*bit_clear1"
  [(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))
   (clobber (reg:CC CC_REG))
  ]
  ""
  "@
  bclr %N1,%A0
  and %1,%0"
  [(set_attr_alternative "timings"
			 [(if_then_else (eq_attr "cpu" "am34")
					(const_int 66) (const_int 77))
			  (const_int 11)
			 ])
  ]
)

(define_insn "*bit_clear2"
  [(set (match_operand:QI 0 "memory_operand" "=R,T")
	(and:QI
	 (match_dup 0)
	 (not:QI (match_operand:QI 1 "nonmemory_operand" "i,d"))))
   (clobber (reg:CC CC_REG))
  ]
  ""
  "@
  bclr %U1,%A0
  bclr %1,%0"
  [(set_attr_alternative "timings"
			 [(if_then_else (eq_attr "cpu" "am34")
					(const_int 66) (const_int 77))
			  (const_int 66)
			 ])
  ]
)

(define_insn "*bit_set"
  [(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))
   (clobber (reg:CC CC_REG))
  ]
  ""
  "@
  bset %U1,%A0
  or %1,%0"
  [(set_attr_alternative "timings"
			 [(if_then_else (eq_attr "cpu" "am34")
					(const_int 66) (const_int 77))
			  (const_int 11)
			 ])
  ]
)

(define_expand "iorqi3"
  [(parallel [(set (match_operand:QI         0 "nonimmediate_operand")
		   (ior:QI (match_operand:QI 1 "nonimmediate_operand")
			   (match_operand:QI 2 "nonmemory_operand")))
	      (clobber (reg:CC CC_REG))
	     ])
  ]
  ""
  "")

(define_insn "*am33_iorqi3"
  [(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,
		;; helps combine do a better job.
		(match_operand:QI 2 "general_operand" "i,d,ir")))
   (clobber (reg:CC CC_REG))
  ]
  "TARGET_AM33 && 
   ((! MEM_P (operands[2])) || (! MEM_P (operands[1])))"
  "@
  bset %U2,%A0
  bset %2,%0
  or %2,%0"
  [(set_attr_alternative "timings"
			 [(if_then_else (eq_attr "cpu" "am34")
					(const_int 66) (const_int 77))
			  (const_int 66)
			  (const_int 11)
			 ])
  ]
)

(define_insn "*mn10300_iorqi3"
  [(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,
		;; helps combine do a better job.
		(match_operand:QI 2 "general_operand" "i,d,id")))
   (clobber (reg:CC CC_REG))
  ]
  "(! MEM_P (operands[2])) || (! MEM_P (operands[1]))"
  "@
  bset %U2,%A0
  bset %2,%0
  or %2,%0"
  [(set_attr_alternative "timings"
			 [(if_then_else (eq_attr "cpu" "am34")
					(const_int 66) (const_int 77))
			  (const_int 66)
			  (const_int 11)
			 ])
  ]
)

(define_insn "*test_int_bitfield"
  [(set (reg:CC CC_REG)
     (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 \"\";
}"
)

(define_insn "*test_byte_bitfield"
  [(set (reg:CC CC_REG)
     (compare (zero_extract:SI (match_operand:QI 0 "nonimmediate_operand" "R,dx")
			       (match_operand 1 "const_int_operand" "")
			       (match_operand 2 "const_int_operand" ""))
	      (const_int 0)))]
  "mn10300_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 (REG_P (operands[0]))
    output_asm_insn (\"btst %1,%0\", xoperands);
  else
    output_asm_insn (\"btst %U1,%A0\", xoperands);
  return \"\";
}"
  [(set_attr_alternative "timings"
			 [(if_then_else (eq_attr "cpu" "am34")
			 		(const_int 11) (const_int 22))
			  (if_then_else (eq_attr "cpu" "am34")
			  		(const_int 44) (const_int 55))
			 ])
  ]
)

(define_insn "*bit_test"
  [(set (reg:CC CC_REG)
	(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 "timings") (if_then_else (eq_attr "cpu" "am34")
				       (const_int 11) (const_int 22)))]
)

(define_insn "*subreg_bit_test"
  [(set (reg:CC CC_REG)
     (compare (and:SI
	       (subreg:SI (match_operand:QI 0 "nonimmediate_operand" "R,dx") 0)
	       (match_operand:SI 1 "const_8bit_operand" ""))
	      (const_int 0)))]
  ""
  "@
  btst %U1,%A0
  btst %1,%0"
  [(set_attr_alternative "timings"
			 [(if_then_else (eq_attr "cpu" "am34")
					(const_int 44) (const_int 55))
			  (if_then_else (eq_attr "cpu" "am34")
					(const_int 11) (const_int 22))
			 ])
  ]
)


;; ----------------------------------------------------------------------
;; COMPARE AND BRANCH INSTRUCTIONS
;; ----------------------------------------------------------------------

;; We expand the comparison into a single insn so that it will not be split
;; up by reload.
(define_expand "cbranchsi4"
  [(set (pc)
	(if_then_else
	      (match_operator                    0 "ordered_comparison_operator"
			      [(match_operand:SI 1 "register_operand")
			       (match_operand:SI 2 "nonmemory_operand")])
              (label_ref (match_operand          3 ""))
              (pc)))]
  ""
  ""
)

(define_insn_and_split "*cbranchsi4_post_reload"
  [(set (pc)
	(if_then_else (match_operator           3 "ordered_comparison_operator"
                       [(match_operand:SI       0 "register_operand"  "dax")
		        (match_operand:SI       1 "nonmemory_operand" "daxi")])
		      (label_ref (match_operand 2 "" ""))
		      (pc)))
   ]
  ""
  "#"
  "reload_completed"
  [(const_int 0)]
  "
  /* We construct the split by hand as otherwise the JUMP_LABEL
     attribute is not set correctly on the jump insn.  */
  emit_insn (gen_cmpsi (operands[0], operands[1]));
  
  emit_jump_insn (gen_integer_conditional_branch
                      (gen_rtx_fmt_ee (GET_CODE (operands[3]),
				       CCmode,
		 		       gen_rtx_REG (CCmode, CC_REG),
				  	            const0_rtx),
				       operands[2]));
  "
)

;; 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 (reg:CC CC_REG)
	(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 mn10300_output_cmp (operands[0], insn);
    return \"cmp %1,%0\";
  }
  [(set_attr_alternative "timings"
			 [(const_int 11)
			  (if_then_else (eq_attr "cpu" "am34")
					(const_int 11) (const_int 22))
			  (const_int 22)
			 ])
  ]
)

(define_insn "integer_conditional_branch"
  [(set (pc)
	(if_then_else (match_operator 0 "comparison_operator"
				      [(reg:CC CC_REG) (const_int 0)])
		      (label_ref (match_operand 1 "" ""))
		      (pc)))]
  ""
  "b%b0 %1"
)

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

(define_insn_and_split "*cbranchsf4_post_reload"
  [(set (pc)
	(if_then_else (match_operator            3 "ordered_comparison_operator"
			[(match_operand:SF       0 "register_operand"  "f")
			 (match_operand:SF       1 "nonmemory_operand" "fF")])
		      (label_ref (match_operand  2 "" ""))
		      (pc)))
   ]
  "TARGET_AM33_2"
  "#"
  "&& reload_completed"
  [(const_int 0)]
  "
  /* We construct the split by hand as otherwise the JUMP_LABEL
     attribute is not set correctly on the jump insn.  */
  emit_insn (gen_am33_cmpsf (operands[0], operands[1]));
  
  emit_jump_insn (gen_float_conditional_branch
                     (gen_rtx_fmt_ee (GET_CODE (operands[3]),
				      CC_FLOATmode,
 		 	       	      gen_rtx_REG (CC_FLOATmode, CC_REG),
				      const0_rtx),
				      operands[2]));
  "
)

(define_insn "am33_cmpsf"
  [(set (reg:CC_FLOAT CC_REG)
	(compare:CC_FLOAT (match_operand:SF 0 "register_operand"  "f")
			  (match_operand:SF 1 "nonmemory_operand" "fF")))]
  "TARGET_AM33_2"
  "fcmp %1, %0"
  [(set (attr "timings") (if_then_else (eq_attr "cpu" "am34")
				       (const_int 17) (const_int 25)))]
)

(define_insn "float_conditional_branch"
  [(set (pc)
	(if_then_else (match_operator 0 "comparison_operator"
				      [(reg:CC_FLOAT CC_REG) (const_int 0)])
		      (label_ref (match_operand 1 "" ""))
		      (pc)))]
  "TARGET_AM33_2"
  "fb%b0 %1"
  [(set (attr "timings") (if_then_else (eq_attr "cpu" "am34")
				       (const_int 44) (const_int 33)))]
)

;; Unconditional and other jump instructions.

(define_insn "jump"
  [(set (pc)
	(label_ref (match_operand 0 "" "")))]
  ""
  "jmp %l0"
  [(set (attr "timings") (if_then_else (eq_attr "cpu" "am34")
				       (const_int 11) (const_int 44)))]
)

(define_insn "indirect_jump"
  [(set (pc) (match_operand:SI 0 "register_operand" "a"))]
  ""
  "jmp (%0)"
  [(set (attr "timings") (if_then_else (eq_attr "cpu" "am34")
				       (const_int 11) (const_int 33)))]
)

(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_insn (gen_addsi3 (index, operands[0], GEN_INT (- 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_insn (gen_ashlsi3 (index, index, const2_rtx));
  emit_move_insn (addr, gen_rtx_MEM (SImode,
				     gen_rtx_PLUS (SImode, table, index)));
  if (flag_pic)
    emit_insn (gen_addsi3 (addr, 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 "timings") (if_then_else (eq_attr "cpu" "am34")
				       (const_int 11) (const_int 33)))]
)

;; Call subroutine with no return value.

(define_expand "call"
  [(call (match_operand:QI 0 "general_operand")
	 (match_operand:SI 1 "general_operand"))]
  ""
{
  rtx fn = XEXP (operands[0], 0);

  if (flag_pic && GET_CODE (fn) == SYMBOL_REF)
    {
      if (MN10300_GLOBAL_P (fn))
	{
	  /* 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);
	  fn = gen_rtx_UNSPEC (SImode, gen_rtvec (1, fn), UNSPEC_PLT);
	}
      else
	fn = gen_rtx_UNSPEC (SImode, gen_rtvec (1, fn), UNSPEC_PIC);
    }
  if (! call_address_operand (fn, VOIDmode))
    fn = force_reg (SImode, fn);

  XEXP (operands[0], 0) = fn;
})

(define_insn "*call_internal"
  [(call (mem:QI (match_operand:SI 0 "call_address_operand" "a,S"))
	 (match_operand:SI 1 "" ""))]
  ""
  "@
   calls %C0
   call %C0,[],0"
  [(set_attr_alternative "timings"
			 [(if_then_else (eq_attr "cpu" "am34")
					(const_int 33) (const_int 44))
			  (if_then_else (eq_attr "cpu" "am34")
					(const_int 55) (const_int 33))
			 ])
  ]
)

;; 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")))]
  ""
{
  rtx fn = XEXP (operands[1], 0);

  if (flag_pic && GET_CODE (fn) == SYMBOL_REF)
    {
      if (MN10300_GLOBAL_P (fn))
	{
	  /* 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);
	  fn = gen_rtx_UNSPEC (SImode, gen_rtvec (1, fn), UNSPEC_PLT);
	}
      else
	fn = gen_rtx_UNSPEC (SImode, gen_rtvec (1, fn), UNSPEC_PIC);
    }
  if (! call_address_operand (fn, VOIDmode))
    fn = force_reg (SImode, fn);

  XEXP (operands[1], 0) = fn;
})

(define_insn "call_value_internal"
  [(set (match_operand 0 "" "")
	(call (mem:QI (match_operand:SI 1 "call_address_operand" "a,S"))
	      (match_operand:SI 2 "" "")))]
  ""
  "@
   calls %C1
   call %C1,[],0"
  [(set_attr_alternative "timings"
			 [(if_then_else (eq_attr "cpu" "am34")
					(const_int 33) (const_int 44))
			  (if_then_else (eq_attr "cpu" "am34")
					(const_int 55) (const_int 33))
			 ])
  ]
)

(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"
)

;; ----------------------------------------------------------------------
;; EXTEND INSTRUCTIONS
;; ----------------------------------------------------------------------

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

(define_insn "*zero_extendqisi2_am33"
  [(set (match_operand:SI 0 "register_operand" "=dx,dx,dx,!dax,!dax,!dax")
	(zero_extend:SI
	 (match_operand:QI 1 "nonimmediate_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_alternative "timings"
			 [(const_int 11)
			  (const_int 22)
			  (if_then_else (eq_attr "cpu" "am34")
					(const_int 13) (const_int 24))
			  (const_int 11)
			  (const_int 22)
			  (if_then_else (eq_attr "cpu" "am34")
					(const_int 13) (const_int 24))
			 ])
  ]
)

(define_insn "*zero_extendqisi2_mn10300"
  [(set (match_operand:SI 0 "register_operand" "=dx,dx,dx")
	(zero_extend:SI
	 (match_operand:QI 1 "nonimmediate_operand" "0,d,m")))]
  ""
  "@
  extbu %0
  mov %1,%0\;extbu %0
  movbu %1,%0"
  [(set_attr_alternative "timings"
			 [(const_int 11)
			  (const_int 22)
			  (if_then_else (eq_attr "cpu" "am34")
					(const_int 13) (const_int 24))
			 ])
  ]
)

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

(define_insn "*zero_extendhisi2_am33"
  [(set (match_operand:SI 0 "register_operand" "=dx,dx,dx,!dax,!dax,!dax")
	(zero_extend:SI
	 (match_operand:HI 1 "nonimmediate_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_alternative "timings"
			 [(const_int 11)
			  (const_int 22)
			  (if_then_else (eq_attr "cpu" "am34")
					(const_int 13) (const_int 24))
			  (const_int 11)
			  (const_int 22)
			  (if_then_else (eq_attr "cpu" "am34")
					(const_int 13) (const_int 24))
			 ])
  ]
)

(define_insn "*zero_extendhisi2_mn10300"
  [(set (match_operand:SI 0 "register_operand" "=dx,dx,dx")
	(zero_extend:SI
	 (match_operand:HI 1 "nonimmediate_operand" "0,dx,m")))]
  ""
  "@
  exthu %0
  mov %1,%0\;exthu %0
  movhu %1,%0"
  [(set_attr_alternative "timings"
			 [(const_int 11)
			  (const_int 22)
			  (if_then_else (eq_attr "cpu" "am34")
					(const_int 13) (const_int 24))
			 ])
  ]
)

;;- sign extension instructions

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

(define_insn "*extendqisi2_am33"
  [(set (match_operand:SI 0 "register_operand" "=dx,dx,!dax,!dax")
	(sign_extend:SI
	 (match_operand:QI 1 "register_operand" "0,dx,0,dax")))]
  "TARGET_AM33"
  "@
  extb %0
  mov %1,%0\;extb %0
  extb %0
  mov %1,%0\;extb %0"
  [(set_attr "timings" "11,22,11,22")]
)

(define_insn "*extendqisi2_mn10300"
  [(set (match_operand:SI 0 "register_operand" "=dx,dx")
	(sign_extend:SI
	 (match_operand:QI 1 "register_operand" "0,dx")))]
  ""
  "@
  extb %0
  mov %1,%0\;extb %0"
  [(set_attr "timings" "11,22")]
)

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

(define_insn "*extendhisi2_am33"
  [(set (match_operand:SI 0 "register_operand" "=dx,dx,!dax,!dax")
	(sign_extend:SI
	 (match_operand:HI 1 "register_operand" "0,dax,0,dax")))]
  "TARGET_AM33"
  "@
  exth %0
  mov %1,%0\;exth %0
  exth %0
  mov %1,%0\;exth %0"
  [(set_attr "timings" "11,22,11,22")]
)

(define_insn "*extendhisi2_mn10300"
  [(set (match_operand:SI 0 "register_operand" "=dx,dx")
	(sign_extend:SI
	 (match_operand:HI 1 "register_operand" "0,dx")))]
  ""
  "@
  exth %0
  mov %1,%0\;exth %0"
  [(set_attr "timings" "11,22")]
)

;; ----------------------------------------------------------------------
;; SHIFTS
;; ----------------------------------------------------------------------

(define_expand "ashlsi3"
  [(parallel [(set (match_operand:SI 0 "register_operand")
		   (ashift:SI
		    (match_operand:SI 1 "register_operand")
		    (match_operand:QI 2 "nonmemory_operand")))
	      (clobber (reg:CC CC_REG))
	     ])
  ]
  ""
  "")

(define_insn "*am33_ashlsi3"
  [(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")))
   (clobber (reg:CC CC_REG))
  ]
  "TARGET_AM33"
  "*
  {
    if (CONST_INT_P (operands[2]) && INTVAL (operands[2]) == 1)
      return \"add %0,%0\";

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

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

    if (CONST_INT_P (operands[2]) && 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 "timings" "22")]
)

(define_insn "*mn10300_ashlsi3"
  [(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")))
   (clobber (reg:CC CC_REG))
  ]
  ""
  "@
  add %0,%0
  asl2 %0
  asl2 %0\;add %0,%0
  asl2 %0\;asl2 %0
  asl %S2,%0"
  [(set_attr "timings" "11,11,22,22,11")]
)

(define_expand "lshrsi3"
  [(parallel [(set (match_operand:SI 0 "register_operand")
		   (lshiftrt:SI
		    (match_operand:SI 1 "register_operand")
		    (match_operand:QI 2 "nonmemory_operand")))
	      (clobber (reg:CC CC_REG))
	     ])
  ]
  ""
  "")

(define_insn "*am33_lshrsi3"
  [(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")))
   (clobber (reg:CC CC_REG))
  ]
  "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 "timings" "22")]
)

(define_insn "*mn10300_lshrsi3"
  [(set (match_operand:SI 0 "register_operand" "=dx")
	(lshiftrt:SI
	 (match_operand:SI 1 "register_operand" "0")
	 (match_operand:QI 2 "nonmemory_operand" "dxi")))
   (clobber (reg:CC CC_REG))
  ]
  ""
  "lsr %S2,%0"
  [(set (attr "timings") (if_then_else (eq_attr "cpu" "am34")
				       (const_int 11) (const_int 22)))]
)

(define_expand "ashrsi3"
  [(parallel [(set (match_operand:SI 0 "register_operand")
		   (ashiftrt:SI
		    (match_operand:SI 1 "register_operand")
		    (match_operand:QI 2 "nonmemory_operand")))
	      (clobber (reg:CC CC_REG))
	     ])
  ]
  ""
  "")

(define_insn "*am33_ashrisi3"
  [(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")))
   (clobber (reg:CC CC_REG))
  ]
  "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 "timings" "22")]
)

(define_insn "*mn10300_ashrsi3"
  [(set (match_operand:SI 0 "register_operand" "=dx")
	(ashiftrt:SI
	 (match_operand:SI 1 "register_operand" "0")
	 (match_operand:QI 2 "nonmemory_operand" "dxi")))
   (clobber (reg:CC CC_REG))
  ]
  ""
  "asr %S2,%0"
  [(set (attr "timings") (if_then_else (eq_attr "cpu" "am34")
				       (const_int 11) (const_int 22)))]
)

;; ----------------------------------------------------------------------
;; 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 "timings") (if_then_else (eq_attr "cpu" "am34")
				       (const_int 17) (const_int 14)))]
)

(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 "timings") (if_then_else (eq_attr "cpu" "am34")
				       (const_int 17) (const_int 14)))]
)

(define_expand "sqrtsf2"
  [(parallel [(set (match_operand:SF          0 "register_operand" "")
		   (sqrt:SF (match_operand:SF 1 "register_operand" "")))
	      (clobber (reg:CC_FLOAT CC_REG))
	     ])
  ]
  "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"))))
   (clobber (reg:CC_FLOAT CC_REG))
  ]
  "TARGET_AM33_2"
  "@
   frsqrt %0
   frsqrt %1, %0"
  [(set (attr "timings") (if_then_else (eq_attr "cpu" "am34")
				       (const_int 4753) (const_int 2327)))]
)

(define_expand "addsf3"
  [(parallel [(set (match_operand:SF          0 "register_operand")
		   (plus:SF (match_operand:SF 1 "register_operand")
			    (match_operand:SF 2 "nonmemory_operand")))
	      (clobber (reg:CC_FLOAT CC_REG))])
  ]
  "TARGET_AM33_2"
  ""
)

(define_insn "*addsf3_internal"
  [(set (match_operand:SF          0 "register_operand" "=f,f")
	(plus:SF (match_operand:SF 1 "register_operand" "%0,f")
		 (match_operand:SF 2 "nonmemory_operand" "f,?fF")))
   (clobber (reg:CC_FLOAT CC_REG))
  ]
  "TARGET_AM33_2"
  "@
   fadd %2, %0
   fadd %2, %1, %0"
  [(set_attr_alternative "timings"
			 [(if_then_else (eq_attr "cpu" "am34")
					(const_int 17) (const_int 14))
			  (if_then_else (eq_attr "cpu" "am34")
					(const_int 17) (const_int 25))
			 ])
  ]
)

(define_expand "subsf3"
  [(parallel [(set (match_operand:SF           0 "register_operand")
		   (minus:SF (match_operand:SF 1 "register_operand")
			     (match_operand:SF 2 "nonmemory_operand")))
	      (clobber (reg:CC_FLOAT CC_REG))])
  ]
  "TARGET_AM33_2"
  ""
)

(define_insn "*subsf3_internal"
  [(set (match_operand:SF           0 "register_operand" "=f,f")
	(minus:SF (match_operand:SF 1 "register_operand" "0,f")
		  (match_operand:SF 2 "nonmemory_operand" "f,?fF")))
   (clobber (reg:CC_FLOAT CC_REG))
  ]
  "TARGET_AM33_2"
  "@
   fsub %2, %0
   fsub %2, %1, %0"
  [(set_attr_alternative "timings"
			 [(if_then_else (eq_attr "cpu" "am34")
					(const_int 17) (const_int 14))
			  (if_then_else (eq_attr "cpu" "am34")
					(const_int 17) (const_int 25))
			 ])
  ]
)

(define_expand "mulsf3"
  [(parallel [(set (match_operand:SF          0 "register_operand")
		   (mult:SF (match_operand:SF 1 "register_operand")
			    (match_operand:SF 2 "nonmemory_operand")))
	      (clobber (reg:CC_FLOAT CC_REG))])
  ]
  "TARGET_AM33_2"
  ""
)

(define_insn "*mulsf3_internal"
  [(set (match_operand:SF          0 "register_operand" "=f,f")
	(mult:SF (match_operand:SF 1 "register_operand" "%0,f")
		 (match_operand:SF 2 "nonmemory_operand" "f,?fF")))
  (clobber (reg:CC_FLOAT CC_REG))
  ]
  "TARGET_AM33_2"
  "@
   fmul %2, %0
   fmul %2, %1, %0"
  [(set_attr_alternative "timings"
			 [(if_then_else (eq_attr "cpu" "am34")
					(const_int 17) (const_int 14))
			  (if_then_else (eq_attr "cpu" "am34")
					(const_int 17) (const_int 25))
			 ])
  ]
)

(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 "nonmemory_operand" "f,?fF")))
   (clobber (reg:CC_FLOAT CC_REG))
  ]
  "TARGET_AM33_2"
  "@
   fdiv %2, %0
   fdiv %2, %1, %0"
  [(set_attr_alternative "timings"
			 [(if_then_else (eq_attr "cpu" "am34")
					(const_int 2531) (const_int 1216))
			  (if_then_else (eq_attr "cpu" "am34")
					(const_int 2531) (const_int 1317))
			 ])
  ]
)

(define_insn "fmasf4"
  [(set (match_operand:SF         0 "register_operand" "=A")
	(fma:SF (match_operand:SF 1 "register_operand" "f")
		(match_operand:SF 2 "register_operand" "f")
		(match_operand:SF 3 "register_operand" "f")))
   (clobber (reg:CC_FLOAT CC_REG))
  ]
  "TARGET_AM33_2"
  "fmadd %1, %2, %3, %0"
  [(set (attr "timings") (if_then_else (eq_attr "cpu" "am34")
				       (const_int 17) (const_int 24)))]
)

(define_insn "fmssf4"
  [(set (match_operand:SF                 0 "register_operand" "=A")
	(fma:SF (match_operand:SF         1 "register_operand" "f")
		(match_operand:SF         2 "register_operand" "f")
		(neg:SF (match_operand:SF 3 "register_operand" "f"))))
   (clobber (reg:CC_FLOAT CC_REG))
  ]
  "TARGET_AM33_2"
  "fmsub %1, %2, %3, %0"
  [(set (attr "timings") (if_then_else (eq_attr "cpu" "am34")
				       (const_int 17) (const_int 24)))]
)

(define_insn "fnmasf4"
  [(set (match_operand:SF                 0 "register_operand" "=A")
	(fma:SF (neg:SF (match_operand:SF 1 "register_operand" "f"))
		(match_operand:SF         2 "register_operand" "f")
		(match_operand:SF         3 "register_operand" "f")))
   (clobber (reg:CC_FLOAT CC_REG))
  ]
  "TARGET_AM33_2"
  "fnmadd %1, %2, %3, %0"
  [(set (attr "timings") (if_then_else (eq_attr "cpu" "am34")
				       (const_int 17) (const_int 24)))]
)

(define_insn "fnmssf4"
  [(set (match_operand:SF                 0 "register_operand" "=A")
	(fma:SF (neg:SF (match_operand:SF 1 "register_operand" "f"))
		(match_operand:SF         2 "register_operand" "f")
		(neg:SF (match_operand:SF 3 "register_operand" "f"))))
   (clobber (reg:CC_FLOAT CC_REG))
  ]
  "TARGET_AM33_2"
  "fnmsub %1, %2, %3, %0"
  [(set (attr "timings") (if_then_else (eq_attr "cpu" "am34")
				       (const_int 17) (const_int 24)))]
)

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

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

(define_insn "return_internal"
  [(const_int 2)
   (return)]
  ""
  "rets"
  [(set_attr "timings" "66")]
)

;; 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 \"\";
  }"
  ;; Assumes that there will be no more than 8 regs to pop
  [(set (attr "timings") (if_then_else (eq_attr "cpu" "am34")
				       (const_int 1414) (const_int 1313)))]
)

;; This instruction matches one generated by mn10300_gen_multiple_store()
(define_insn "store_movm"
  [(match_parallel 0 "mn10300_store_multiple_operation"
    [(set (reg:SI SP_REG) (plus:SI (reg:SI SP_REG) (match_operand 1 "" "")))])]
  ""
  "*
  {
    fputs (\"\\tmovm \", asm_out_file);
    mn10300_print_reg_list (asm_out_file,
                            mn10300_store_multiple_operation (operands[0],
						              VOIDmode));
    fprintf (asm_out_file, \",(sp)\\n\");
    return \"\";
  }"
  ;; Assume that no more than 8 registers will be pushed.
  [(set (attr "timings") (if_then_else (eq_attr "cpu" "am34")
				       (const_int 99) (const_int 88)))]
)

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

  if (next
      && JUMP_P (next)
      && GET_CODE (PATTERN (next)) == RETURN)
    return \"\";
  else
    return \"rets\";
}"
  [(set_attr "timings" "66")]
)

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

(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 "" ""))])]
  "! TARGET_AM33"
  "#"
  "&& 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_insn (gen_addsi3 (sp_reg, sp_reg, GEN_INT (-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:"
  [(set_attr "timings" "44")]
)

(define_expand "add_GOT_to_pic_reg"
  [(parallel [(set (reg:SI PIC_REG)
		   (plus:SI
		    (reg:SI PIC_REG)
		    (const:SI
		     (unspec:SI [(minus:SI
			       (match_dup 1)
			       (const (minus:SI
				       (const (match_operand:SI 0 "" ""))
				       (pc))))
			      ] UNSPEC_PIC))))
	      (clobber (reg:CC CC_REG))
	      ])
  ]
  ""
  "operands[1] = gen_rtx_SYMBOL_REF (VOIDmode, GOT_SYMBOL_NAME);"
)

(define_expand "add_GOT_to_any_reg"
  [(parallel [(set (match_operand:SI 0 "" "")
		   (plus:SI
		    (match_operand:SI 1 "" "")
		    (const
		     (unspec [(minus:SI
			       (match_dup 3)
			       (const (minus:SI
				       (const (match_operand:SI 2 "" ""))
				       (pc))))
			      ] UNSPEC_PIC))))
	      (clobber (reg:CC CC_REG))
	     ])
  ]
  ""
  "operands[3] = gen_rtx_SYMBOL_REF (VOIDmode, GOT_SYMBOL_NAME);"
)
