;; GCC machine description for Hitachi H8/300
;; Copyright (C) 1992, 1993, 1994, 1995 Free Software Foundation, Inc.

;;   Contributed by Steve Chamberlain (sac@cygnus.com),
;;   Jim Wilson (wilson@cygnus.com), and Doug Evans (dje@cygnus.com).

;; This file is part of GNU CC.

;; GNU CC is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 2, or (at your option)
;; any later version.

;; GNU CC 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 GNU CC; see the file COPYING.  If not, write to
;; the Free Software Foundation, 59 Temple Place - Suite 330,
;; Boston, MA 02111-1307, USA.


;; 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_attr "cpu" "h8300,h8300h"
  (const (symbol_ref "cpu_type")))

;; ??? If we can remove the operand type on all the insns, do it.
;; ??? Otherwise, try to have the operand type on all the insns.

(define_attr "type" "branch,return,call,arith,move,float,multi"
  (const_string "arith"))

;; The size of instructions in bytes.

(define_attr "length" "" 
  (cond [(eq_attr "type" "branch")
	 (if_then_else (and (ge (minus (pc) (match_dup 0))
				(const_int -120))
			    (le (minus (pc) (match_dup 0))
				(const_int 120)))
		       (const_int 2)
		       (if_then_else (and (eq_attr "cpu" "h8300h")
					  (and (ge (minus (pc) (match_dup 0))
						   (const_int -32000))
					       (le (minus (pc) (match_dup 0))
						   (const_int 32000))))
				     (const_int 4)
				     (const_int 6)))
	 (eq_attr "type" "move")	(const_int 4)
	 (eq_attr "type" "return")	(const_int 2)
	 (eq_attr "type" "float")	(const_int 12)
	 (eq_attr "type" "call")	(const_int 4)]
	(const_int 200)))

(define_attr "cc" "none,clobber,none_0hit,set,compare,whoops,cbit" 
  (const_string "whoops"))

;; ----------------------------------------------------------------------
;; MOVE INSTRUCTIONS
;; ----------------------------------------------------------------------

;; movqi

(define_insn "movqi_push"
  [(set (match_operand:QI 0 "push_operand" "=<")
	(match_operand:QI 1 "register_operand" "r"))]
  ""
  "*
{
  if (TARGET_H8300)
    return \"push.w	%T1\";
  else
    return \"push.l	%S1\";
}"
  [(set_attr "type" "move")
   (set (attr "length") (if_then_else (eq_attr "cpu" "h8300") (const_int 2) (const_int 4)))
   (set_attr "cc" "set")])

(define_insn "movqi_internal"
  [(set (match_operand:QI 0 "general_operand_dst" "=r,r,r,o,<,r")
	(match_operand:QI 1 "general_operand_src" "I,r>,io,r,r,c"))]
  "register_operand (operands[0],QImode) || register_operand (operands[1], QImode)"
  "@
   sub.b	%X0,%X0
   mov.b	%X1,%X0
   mov.b	%X1,%X0
   mov.b	%X1,%X0
   mov.b	%X1,%X0
   xor		%X0,%X0\;bst	#0,%X0"
  [(set_attr "type" "move")
   (set_attr_alternative "length"
     [(const_int 2) (const_int 2)
      (if_then_else (eq_attr "cpu" "h8300") (const_int 4) (const_int 8))
      (if_then_else (eq_attr "cpu" "h8300") (const_int 4) (const_int 8))
      (if_then_else (eq_attr "cpu" "h8300") (const_int 2) (const_int 4))
      (const_int 4)])
   (set_attr "cc" "set,set,set,set,set,none")])

(define_expand "movqi"
  [(set (match_operand:QI 0 "general_operand_dst" "")
	(match_operand:QI 1 "general_operand_src" ""))]
  ""
  "
{
  /* 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 "movstrictqi"
  [(set (strict_low_part (match_operand:QI 0 "general_operand_dst" "=r,r,r,o,<"))
			 (match_operand:QI 1 "general_operand_src" "I,r,io,r,r"))]
  ""
  "@
   sub.b	%X0,%X0
   mov.b	%X1,%X0
   mov.b	%X1,%X0
   mov.b	%X1,%X0
   mov.b	%X1,%X0"
  [(set_attr "type" "move")
   (set_attr_alternative "length"
     [(const_int 2) (const_int 2)
      (if_then_else (eq_attr "cpu" "h8300") (const_int 4) (const_int 8))
      (if_then_else (eq_attr "cpu" "h8300") (const_int 4) (const_int 8))
      (if_then_else (eq_attr "cpu" "h8300") (const_int 2) (const_int 4))])
   (set_attr "cc" "set")])
   
;; movhi

(define_insn "movhi_push"
  [(set (match_operand:HI 0 "push_operand" "=<")
	(match_operand:HI 1 "register_operand" "ra"))]
  ""
  "*
{
  if (TARGET_H8300)
    return \"push.w	%T1\";
  else
    return \"push.l	%S1\";
}"
  [(set_attr "type" "move")
   (set (attr "length") (if_then_else (eq_attr "cpu" "h8300") (const_int 2) (const_int 4)))
   (set_attr "cc" "set")])

(define_insn "movhi_internal"
  [(set (match_operand:HI 0 "general_operand_dst" "=ra,ra,ra,o,<")
	(match_operand:HI 1 "general_operand_src" "I,ra>,ion,ra,ra"))]
  ""
  "@
   sub.w	%T0,%T0
   mov.w	%T1,%T0
   mov.w	%T1,%T0
   mov.w	%T1,%T0
   mov.w	%T1,%T0"
  [(set_attr "type" "move")
   (set_attr_alternative "length"
     [(const_int 2) (const_int 2)
      (if_then_else (eq_attr "cpu" "h8300") (const_int 4) (const_int 8))
      (if_then_else (eq_attr "cpu" "h8300") (const_int 4) (const_int 8))
      (if_then_else (eq_attr "cpu" "h8300") (const_int 2) (const_int 4))])
   (set_attr "cc" "set")])

(define_expand "movhi"
  [(set (match_operand:HI 0 "general_operand_dst" "")
	(match_operand:HI 1 "general_operand_src" ""))]
  ""
  "
{
  /* 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 "movstricthi"
  [(set (strict_low_part (match_operand:HI 0 "general_operand_dst" "=r,r,r,o,<"))
			 (match_operand:HI 1 "general_operand_src" "I,r,io,r,r"))]
  ""
  "@
   sub.w	%T0,%T0
   mov.w	%T1,%T0
   mov.w	%T1,%T0
   mov.w	%T1,%T0
   mov.w	%T1,%T0"
  [(set_attr "type" "move")
   (set_attr_alternative "length"
     [(const_int 2) (const_int 2)
      (if_then_else (eq_attr "cpu" "h8300") (const_int 4) (const_int 8))
      (if_then_else (eq_attr "cpu" "h8300") (const_int 4) (const_int 8))
      (if_then_else (eq_attr "cpu" "h8300") (const_int 2) (const_int 4))])
   (set_attr "cc" "set")])

;; movsi

(define_expand "movsi"
  [(set (match_operand:SI 0 "general_operand_dst" "")
	(match_operand:SI 1 "general_operand_src" ""))]
  ""
  "
{
  if (TARGET_H8300)
    {
      if (do_movsi (operands))
	DONE;
    }
  else /* TARGET_H8300H */
    {
      /* 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);
	}
    }
}")

(define_expand "movsf"
  [(set (match_operand:SF 0 "general_operand_dst" "")
	(match_operand:SF 1 "general_operand_src" ""))]
  ""
  "
{
  if (TARGET_H8300)
    {
      if (do_movsi (operands))
	DONE;
    }
  else /* TARGET_H8300H */
    {
      /* 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 "movsi_h8300"
  [(set (match_operand:SI 0 "general_operand_dst" "=r,r,r,o,<,r")
	(match_operand:SI 1 "general_operand_src" "I,r,ion,r,r,>"))]
  "TARGET_H8300
   && (register_operand (operands[0], SImode)
       || register_operand (operands[1], SImode))"
  "*
{
  int rn = -1;
  switch (which_alternative)
    {
    case 0:
      return \"sub.w	%e0,%e0\;sub.w	%f0,%f0\";
    case 1:
      if (REGNO(operands[0]) < REGNO(operands[1]))
	return \"mov.w	%e1,%e0\;mov.w	%f1,%f0\";
      else 
	return \"mov.w	%f1,%f0\;mov.w	%e1,%e0\";
    case 2:
      /* Make sure we don't trample the register we index with.  */
    
      if (GET_CODE(operands[1]) == MEM) 
	{
	  rtx inside = XEXP (operands[1],0);
	  if  (REG_P (inside)) 
	    {
	      rn = REGNO(inside);
	    }
	  else if (GET_CODE (inside) == PLUS) 
	    {
	      rtx lhs = XEXP (inside,0);
	      rtx rhs = XEXP (inside,1);
	      if (REG_P (lhs)) rn = REGNO (lhs);
	      if (REG_P (rhs)) rn = REGNO (rhs);
	    }
	}
      if (rn == REGNO (operands[0])) 	
	{
	  /* Move the second word first.  */
	  return \"mov.w	%f1,%f0\;mov.w	%e1,%e0\";
	}
      else 
	{
	  return \"mov.w	%e1,%e0\;mov.w	%f1,%f0\";
	}
    
    case 3:
      return \"mov.w	%e1,%e0\;mov.w	%f1,%f0\";
    case 4:
      return \"mov.w	%f1,%T0\;mov.w	%e1,%T0\";
    case 5:
      return \"mov.w	%T1,%e0\;mov.w	%T1,%f0\";
    }
}"
  [(set_attr "type" "move")
   (set_attr "length" "4,4,8,8,4,4")
   (set_attr "cc" "clobber")])

(define_insn "movsf_h8300"
  [(set (match_operand:SF 0 "general_operand_dst" "=r,r,r,o,<,r")
	(match_operand:SF 1 "general_operand_src" "I,r,ion,r,r,>"))]
  "TARGET_H8300
   && (register_operand (operands[0], SFmode)
       || register_operand (operands[1], SFmode))"
  "*
{
  /* Copy of the movsi stuff */
  int rn = -1;
  switch (which_alternative)
    {
    case 0:
      return \"sub.w	%e0,%e0\;sub.w	%f0,%f0\";
    case 1:
      if (REGNO(operands[0]) < REGNO(operands[1]))
	return \"mov.w	%e1,%e0\;mov.w	%f1,%f0\";
      else 
	return \"mov.w	%f1,%f0\;mov.w	%e1,%e0\";
    case 2:
      /* Make sure we don't trample the register we index with.  */
    
      if (GET_CODE (operands[1]) == MEM) 
	{
	  rtx inside = XEXP (operands[1],0);
	  if (REG_P (inside))
	    {
	      rn = REGNO (inside);
	    }
	  else if (GET_CODE (inside) == PLUS) 
	    {
	      rtx lhs = XEXP (inside,0);
	      rtx rhs = XEXP (inside,1);
	      if (REG_P (lhs)) rn = REGNO (lhs);
	      if (REG_P (rhs)) rn = REGNO (rhs);
	    }
	}
      if (rn == REGNO (operands[0]))
	{
	  /* move the second word first */
	  return \"mov.w	%f1,%f0\;mov.w	%e1,%e0\";
	}
      else 
	{
	  return \"mov.w	%e1,%e0\;mov.w	%f1,%f0\";
	}
    
    case 3:
      return \"mov.w	%e1,%e0\;mov.w	%f1,%f0\";
    case 4:
      return \"mov.w	%f1,%T0\;mov.w	%e1,%T0\";
    case 5:
      return \"mov.w	%T1,%e0\;mov.w	%T1,%f0\";

    }
}"
  [(set_attr "type" "move")
   (set_attr "length" "4,4,8,8,4,4")
   (set_attr "cc" "clobber")])

(define_insn "movsi_h8300h"
  [(set (match_operand:SI 0 "general_operand_dst" "=ra,ra,ra,o,<,ra")
	(match_operand:SI 1 "general_operand_src" "I,ra,ion,ra,ra,>"))]
  "TARGET_H8300H
   && (register_operand (operands[0], SImode)
       || register_operand (operands[1], SImode))"
  "@
   sub.l	%S0,%S0
   mov.l	%S1,%S0
   mov.l	%S1,%S0
   mov.l	%S1,%S0
   mov.l	%S1,%S0
   mov.l	%S1,%S0"
  [(set_attr "type" "move")
   (set_attr "length" "2,2,8,8,4,4")
   (set_attr "cc" "set")])

(define_insn "movsf_h8300h"
  [(set (match_operand:SF 0 "general_operand_dst" "=r,r,r,o,<,r")
	(match_operand:SF 1 "general_operand_src" "I,r,ion,r,r,>"))]
  "TARGET_H8300H
   && (register_operand (operands[0], SFmode)
       || register_operand (operands[1], SFmode))"
  "@
   sub.l	%S0,%S0
   mov.l	%S1,%S0
   mov.l	%S1,%S0
   mov.l	%S1,%S0
   mov.l	%S1,%S0
   mov.l	%S1,%S0"
  [(set_attr "type" "move")
   (set_attr "length" "2,2,8,8,4,4")
   (set_attr "cc" "set")])

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

(define_insn "tstqi"
  [(set (cc0) (match_operand:QI 0 "register_operand" "ra"))]
  ""
  "*
{
  /* ??? I don't think this is right.  --Jim */
  if (cc_prev_status.flags & CC_DONE_CBIT)
    return \"btst	#0,%X0\";
  else
    return \"cmp.b	#0,%X0\";
}"
  [(set_attr "type" "arith")
   (set_attr "length" "4")
   (set_attr "cc" "set")])

(define_insn "tsthi"
  [(set (cc0) (match_operand:HI 0 "general_operand" "ra"))]
  ""
  "*
{
  /* ??? I don't think this is right.  --Jim */
  if (cc_prev_status.flags & CC_DONE_CBIT)
    return \"btst	#0,%0l\";
  else
    return \"mov.w	%T0,%T0\";
}"
  [(set_attr "type" "arith")
   (set_attr "length" "4")
   (set_attr "cc" "set")])

(define_insn "tstsi"
  [(set (cc0) (match_operand:SI 0 "general_operand" "ra"))]
  "TARGET_H8300H"
  "*
{
  /* ??? I don't think this is right.  --Jim */
  if (cc_prev_status.flags & CC_DONE_CBIT)
    return \"btst	#0,%0l\";
  else
    return \"mov.l	%S0,%S0\";
}"
  [(set_attr "type" "arith")
   (set_attr "length" "4")
   (set_attr "cc" "set")])

(define_insn "cmpqi"
  [(set (cc0)
	(compare:QI (match_operand:QI 0 "register_operand" "ra")
		    (match_operand:QI 1 "nonmemory_operand" "rai")))]
  ""
  "cmp.b	%X1,%X0"
  [(set_attr "type" "arith")
   (set_attr "length" "2")
   (set_attr "cc" "compare")])

;; ??? 300h can have an immediate operand here.

(define_insn "cmphi"
  [(set (cc0)
	(compare:HI (match_operand:HI 0 "register_operand" "ra")
		    (match_operand:HI 1 "register_operand" "ra")))]
  ""
  "cmp.w	%T1,%T0"
  [(set_attr "type" "arith")
   (set_attr "length" "2")
   (set_attr "cc" "compare")])

;; ??? 300h can have an immediate operand here.

(define_insn "cmpsi"
  [(set (cc0)
	(compare:SI (match_operand:SI 0 "register_operand" "ra")
		    (match_operand:SI 1 "register_operand" "ra")))]
  "TARGET_H8300H"
  "cmp.l	%S1,%S0"
  [(set_attr "type" "arith")
   (set_attr "length" "2")
   (set_attr "cc" "compare")])

;; ----------------------------------------------------------------------
;; ADD INSTRUCTIONS
;; ----------------------------------------------------------------------

(define_insn "addqi3"
  [(set (match_operand:QI 0 "register_operand" "=r")
	(plus:QI (match_operand:QI 1 "register_operand" "%0")
		 (match_operand:QI 2 "nonmemory_operand" "ri")))]
  ""
  "add.b	%X2,%X0"
  [(set_attr "type" "arith")
   (set_attr "length" "2")
   (set_attr "cc" "set")])

;; ??? adds operates on the 32bit register.  We can use it because we don't
;; use the e0-7 registers.
;; ??? 4 can be handled in one insn on the 300h.

(define_insn "addhi3_internal"
  [(set (match_operand:HI 0 "register_operand" "=ra,ra,ra,ra,r,ra")
	(plus:HI (match_operand:HI 1 "register_operand" "%0,0,0,0,0,0")
		 (match_operand:HI 2 "nonmemory_operand" "K,M,L,N,n,ra")))]
  ""
  "@
   adds	%T2,%A0
   adds	#2,%A0\;adds	%C2,%A0
   subs	%M2,%A0
   subs	#2,%A0\;subs	%M2,%A0
   add.b	%s2,%s0\;addx	%t2,%t0 
   add.w	%T2,%T0"
  [(set_attr "type" "arith,multi,arith,multi,multi,arith")
   (set_attr "length" "2,4,2,4,4,2")
   (set_attr "cc" "none_0hit,none_0hit,none_0hit,none_0hit,clobber,set")])

;; ??? Why is this here?
(define_expand "addhi3"
  [(set (match_operand:HI 0 "register_operand" "")
	(plus:HI (match_operand:HI 1 "register_operand" "")
		 (match_operand:HI 2 "nonmemory_operand" "")))]
  ""
  "")

(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 "addsi_h8300"
  [(set (match_operand:SI 0 "register_operand" "=r,r,&r")
	(plus:SI (match_operand:SI 1 "register_operand" "%0,0,r")
		 (match_operand:SI 2 "nonmemory_operand" "n,r,r")))]
  "TARGET_H8300"
  "@
   add	%w2,%w0\;addx	%x2,%x0\;addx	%y2,%y0\;addx	%z2,%z0
   add.w	%f2,%f0\;addx	%y2,%y0\;addx	%z2,%z0
   mov	%f1,%f0\;mov	%e1,%e0\;add.w	%f2,%f0\;addx	%y2,%y0\;addx	%z2,%z0"
  [(set_attr "type" "arith")
   (set_attr "length" "8,6,20")
   (set_attr "cc" "clobber")])

;; ??? 4 can be handled in one insn on the 300h.
;; ??? Should the 'n' constraint be 'i' here?
;; ??? We don't handle (reg + symbol_ref) which the 300h can handle.

(define_insn "addsi_h8300h"
  [(set (match_operand:SI 0 "register_operand" "=ra,ra,ra,ra,r,ra")
	(plus:SI (match_operand:SI 1 "register_operand" "%0,0,0,0,0,0")
		 (match_operand:SI 2 "nonmemory_operand" "K,M,L,N,n,ra")))]
  "TARGET_H8300H"
  "@
   adds	%S2,%S0
   adds	#2,%S0\;adds	%C2,%S0
   subs	%M2,%S0
   subs	#2,%S0\;subs	%M2,%S0
   add.l	%S2,%S0
   add.l	%S2,%S0"
  [(set_attr "type" "multi,multi,multi,multi,arith,arith")
   (set_attr "length" "2,4,2,4,6,2")
   (set_attr "cc" "none_0hit,none_0hit,none_0hit,none_0hit,clobber,clobber")])

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

(define_insn "subqi3"
  [(set (match_operand:QI 0 "register_operand" "=r,r")
	(minus:QI (match_operand:QI 1 "register_operand" "0,0")
		  (match_operand:QI 2 "nonmemory_operand" "r,i")))]
  ""
  "@
   sub.b	%X2,%X0
   add.b	%G2,%X0"
  [(set_attr "type" "arith")
   (set_attr "length" "2")
   (set_attr "cc" "set")])

;; ??? subs operates on the 32bit register.  We can use it because we don't
;; use the e0-7 registers.
;; ??? 4 can be handled in one insn on the 300h.
;; ??? The fourth alternative can use sub.w on the 300h.
;; ??? Should the 'n' constraint be an 'i' here?

(define_insn "subhi3_internal"
  [(set (match_operand:HI 0 "register_operand" "=ra,ra,ra,r")
	(minus:HI (match_operand:HI 1 "general_operand" "0,0,0,0")
		  (match_operand:HI 2 "nonmemory_operand" "K,M,ra,n")))]
  ""
  "@
   subs	%T2,%T0
   subs	#2,%T0\;subs	%M2,%T0
   sub.w	%T2,%T0
   add.b	%E2,%s0\;addx	%F2,%t0 ; -%0"
  [(set_attr "type" "multi")
   (set_attr "length" "2,4,2,4")
   (set_attr "cc" "none_0hit,none_0hit,set,clobber")])

;; ??? Why is this here?
(define_expand "subhi3"
  [(set (match_operand:HI 0 "register_operand" "")
	(minus:HI (match_operand:HI 1 "register_operand" "")
		  (match_operand:HI 2 "nonmemory_operand" "")))]
  ""
  "")

(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 "subsi3_h8300"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(minus:SI (match_operand:SI 1 "register_operand" "0")
		  (match_operand:SI 2 "register_operand" "r")))]
  "TARGET_H8300"
  "sub.w	%f2,%f0\;subx	%y2,%y0\;subx	%z2,%z0"
  [(set_attr "type" "arith")
   (set_attr "length" "6")
   (set_attr "cc" "clobber")])

;; ??? 4 can be handled in one insn on the 300h.

(define_insn "subsi3_h8300h"
  [(set (match_operand:SI 0 "register_operand" "=ra,ra,ra,r")
	(minus:SI (match_operand:SI 1 "general_operand" "0,0,0,0")
		  (match_operand:SI 2 "nonmemory_operand" "K,M,ra,n")))]
  "TARGET_H8300H"
  "@
   subs	%T2,%T0
   subs	#2,%T0\;subs	%E2,%T0
   sub.l	%S2,%S0
   sub.l	%S2,%S0"
  [(set_attr "type" "multi")
   (set_attr "length" "2,4,2,6")
   (set_attr "cc" "none_0hit,none_0hit,set,set")])

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

;; Note that the h8/300 can only handle umulqihi3.

(define_insn "mulqihi3"
  [(set (match_operand:HI 0 "register_operand" "=r")
	(mult:HI (sign_extend:HI (match_operand:QI 1 "general_operand" "%0"))
		 (sign_extend:HI (match_operand:QI 2 "register_operand" "r"))))]
  "TARGET_H8300H"
  "mulxs.b	%X2,%T0"
  [(set_attr "type" "multi")
   (set_attr "length" "4")
   (set_attr "cc" "set")])

(define_insn "mulhisi3"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(mult:SI (sign_extend:SI (match_operand:HI 1 "general_operand" "%0"))
		 (sign_extend:SI (match_operand:HI 2 "register_operand" "r"))))]
  "TARGET_H8300H"
  "mulxs.w	%T2,%S0"
  [(set_attr "type" "multi")
   (set_attr "length" "4")
   (set_attr "cc" "set")])

(define_insn "umulqihi3"
  [(set (match_operand:HI 0 "register_operand" "=r")
	(mult:HI (zero_extend:HI (match_operand:QI 1 "general_operand" "%0"))
		 (zero_extend:HI (match_operand:QI 2 "register_operand" "r"))))]
  ""
  "mulxu	%X2,%T0"
  [(set_attr "type" "multi")
   (set_attr "length" "2")
   (set_attr "cc" "none_0hit")])

(define_insn "umulhisi3"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(mult:SI (zero_extend:SI (match_operand:HI 1 "general_operand" "%0"))
		 (zero_extend:SI (match_operand:HI 2 "register_operand" "r"))))]
  "TARGET_H8300H"
  "mulxu.w	%T2,%S0"
  [(set_attr "type" "multi")
   (set_attr "length" "2")
   (set_attr "cc" "none_0hit")])

;; ----------------------------------------------------------------------
;; DIVIDE INSTRUCTIONS
;; ----------------------------------------------------------------------

(define_insn "udivqi3"
  [(set (match_operand:QI 0 "register_operand" "=r")
	(udiv:QI (match_operand:HI 1 "general_operand" "0")
		 (match_operand:QI 2 "register_operand" "r")))]
  ""
  "divxu	%X2,%T0"
  [(set_attr "type" "multi")
   (set_attr "length" "2")
   (set_attr "cc" "clobber")])

;; ??? Will divxu always work here?

(define_insn "divqi3"
  [(set (match_operand:QI 0 "register_operand" "=r")
	(div:QI (match_operand:HI 1 "general_operand" "0")
		(match_operand:QI 2 "register_operand" "r")))]
  ""
  "divxu	%X2,%T0"
  [(set_attr "type" "multi")
   (set_attr "length" "2")
   (set_attr "cc" "clobber")])

(define_insn "udivhi3"
  [(set (match_operand:HI 0 "register_operand" "=r")
	(udiv:HI (match_operand:SI 1 "general_operand" "0")
		 (match_operand:HI 2 "register_operand" "r")))]
  "TARGET_H8300H"
  "divxu.w	%T2,%S0"
  [(set_attr "type" "multi")
   (set_attr "length" "2")
   (set_attr "cc" "clobber")])

(define_insn "divhi3"
  [(set (match_operand:HI 0 "register_operand" "=r")
	(div:HI (match_operand:SI 1 "general_operand" "0")
		(match_operand:HI 2 "register_operand" "r")))]
  "TARGET_H8300H"
  "divxs.w	%T2,%S0"
  [(set_attr "type" "multi")
   (set_attr "length" "4")
   (set_attr "cc" "clobber")])

;; ----------------------------------------------------------------------
;; MOD INSTRUCTIONS
;; ----------------------------------------------------------------------

(define_insn "umodqi3"
  [(set (match_operand:QI 0 "register_operand" "=r")
	(umod:QI (match_operand:HI 1 "general_operand" "0")
		 (match_operand:QI 2 "register_operand" "r")))]
  ""
  "divxu	%X2,%T0\;mov %t0,%s0"
  [(set_attr "type" "multi")
   (set_attr "length" "4")
   (set_attr "cc" "clobber")])

(define_insn "modqi3"
  [(set (match_operand:QI 0 "register_operand" "=r")
	(mod:QI (match_operand:HI 1 "general_operand" "0")
		(match_operand:QI 2 "register_operand" "r")))]
  "TARGET_H8300H"
  "divxs.b	%X2,%T0\;mov %t0,%s0"
  [(set_attr "type" "multi")
   (set_attr "length" "6")
   (set_attr "cc" "clobber")])

(define_insn "umodhi3"
  [(set (match_operand:HI 0 "register_operand" "=r")
	(umod:HI (match_operand:SI 1 "general_operand" "0")
		 (match_operand:HI 2 "register_operand" "r")))]
  "TARGET_H8300H"
  "divxu.w	%T2,%S0\;mov %e0,%f0"
  [(set_attr "type" "multi")
   (set_attr "length" "4")
   (set_attr "cc" "clobber")])

(define_insn "modhi3"
  [(set (match_operand:HI 0 "register_operand" "=r")
	(mod:HI (match_operand:SI 1 "general_operand" "0")
		(match_operand:HI 2 "register_operand" "r")))]
  "TARGET_H8300H"
  "divxs.w	%T2,%S0\;mov %e0,%f0"
  [(set_attr "type" "multi")
   (set_attr "length" "6")
   (set_attr "cc" "clobber")])

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

(define_insn "andqi3_internal"
  [(set (match_operand:QI 0 "bit_operand" "=r,U")
	(and:QI (match_operand:QI 1 "bit_operand" "%0,0")
		(match_operand:QI 2 "nonmemory_operand" "rn,O")))]
  "register_operand (operands[0], QImode) || o_operand (operands[2], QImode)"
  "@
   and	%X2,%X0
   bclr	%W2,%X0"
  [(set_attr "type" "arith")
   (set_attr "length" "2,4")
   (set_attr "cc" "set,none_0hit")])

(define_expand "andqi3"
  [(set (match_operand:QI 0 "bit_operand" "=r,U")
	(and:QI (match_operand:QI 1 "bit_operand" "%0,0")
		(match_operand:QI 2 "nonmemory_operand" "rn,O")))]
  ""
  "
{
  if (fix_bit_operand (operands, 'O', AND))
    DONE;
}")

;; ??? Should have a bclr case here also.

(define_insn "andhi3"
  [(set (match_operand:HI 0 "register_operand" "=r")
	(and:HI (match_operand:HI 1 "register_operand" "%0")
		(match_operand:HI 2 "nonmemory_operand" "rn")))]
  ""
  "*
{
  if (GET_CODE (operands[2]) == CONST_INT)
    {
      int i = INTVAL (operands[2]);
      if ((i & 0x00ff) != 0x00ff) 
	output_asm_insn (\"and	%s2,%s0\", operands);
      if ((i & 0xff00) != 0xff00) 
	output_asm_insn (\"and	%t2,%t0\", operands);
      return \"\";
    }
  return \"and	%s2,%s0\;and	%t2,%t0;\";
}"
  [(set_attr "type" "multi")
   (set_attr "length" "4")
   (set_attr "cc" "clobber")])

;; ??? There is an iorsi3 for TARGET_H8300.  Should we have andsi3?

(define_insn "andsi3"
  [(set (match_operand:SI 0 "register_operand" "=r,r")
	(and:SI (match_operand:SI 1 "register_operand" "%0,0")
		(match_operand:SI 2 "nonmemory_operand" "r,i")))]
  "TARGET_H8300H"
  "@
   and	%S2,%S0
   and	%S2,%S0"
  [(set_attr "type" "arith")
   (set_attr "length" "4,6")
   (set_attr "cc" "clobber")])

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

(define_insn "iorqi3_internal"
  [(set (match_operand:QI 0 "bit_operand" "=U,r")
	(ior:QI (match_operand:QI 1 "bit_operand" "%0,0")
		(match_operand:QI 2 "nonmemory_operand" "P,rn")))]
  "register_operand (operands[0], QImode) || p_operand (operands[2], QImode)"
  "@
   bset	%V2,%X0
   or	%X2,%X0"
  [(set_attr "type" "arith")
   (set_attr "length" "4,2")
   (set_attr "cc" "none_0hit,set")])

(define_expand "iorqi3"
  [(set (match_operand:QI 0 "bit_operand" "=r,U")
	(ior:QI (match_operand:QI 1 "bit_operand" "%0,0")
		(match_operand:QI 2 "nonmemory_operand" "rn,P")))]
  ""
  "
{
  if (fix_bit_operand (operands, 'P', IOR))
    DONE;
}")

;; ??? Should have a bset case here also.

(define_insn "iorhi3"
  [(set (match_operand:HI 0 "general_operand" "=r,r")
	(ior:HI (match_operand:HI 1 "general_operand" "%0,0")
		(match_operand:HI 2 "general_operand" "J,rn")))]
  ""
  "*
{
  if (TARGET_H8300)
    {
      if (GET_CODE (operands[2]) == CONST_INT)
	{
	  int i = INTVAL (operands[2]);
	  if ((i & 0x00ff) != 0) 
	    output_asm_insn (\"or	%s2,%s0\", operands);
	  if ((i & 0xff00) != 0) 
	    output_asm_insn (\"or	%t2,%t0\", operands);
	  return \"\";
	}
      return \"or	%s2,%s0\;or	%t2,%t0; %2 or2\";
    }
  else
    {
      return \"or	%S2,%S0\";
    }
}"
  [(set_attr "type" "multi")
   (set_attr "length" "2,4")
   (set_attr "cc" "clobber,clobber")])

(define_insn "iorsi3"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(ior:SI (match_operand:SI 1 "register_operand" "%0")
		(match_operand:SI 2 "nonmemory_operand" "ri")))]
  ""
  "*
{
  if (TARGET_H8300)
    {
      if (GET_CODE (operands[2]) == CONST_INT)
	{
	  int i = INTVAL (operands[2]);
	  if ((i & 0x000000ff) != 0) 
	    output_asm_insn (\"or	%w2,%w0\", operands);
	  if ((i & 0x0000ff00) != 0) 
	    output_asm_insn (\"or	%x2,%x0\", operands);
	  if ((i & 0x00ff0000) != 0) 
	    output_asm_insn (\"or	%y2,%y0\", operands);
	  if ((i & 0xff000000) != 0) 
	    output_asm_insn (\"or	%z2,%z0\", operands);
	  return \"\";
	}
      return \"or	%w2,%w0\;or	%x2,%x0\;or	%y2,%y0\;or	%z2,%z0\;\";
    }
  else
    {
      return \"or	%S2,%S0\";
    }
}"
  [(set_attr "type" "multi")
   (set_attr "length" "8")
   (set_attr "cc" "clobber")])

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

(define_insn "xorqi3_internal"
  [(set (match_operand:QI 0 "bit_operand" "=r,U")
	(xor:QI (match_operand:QI 1 "bit_operand" "%0,0")
		(match_operand:QI 2 "nonmemory_operand" "rn,P")))]
  "register_operand (operands[0], QImode) || p_operand (operands[2], QImode)"
  "@
   xor	%X2,%X0
   bnot	%V2,%X0"
  [(set_attr "type" "arith")
   (set_attr "length" "2,4")
   (set_attr "cc" "set,none_0hit")])

(define_expand "xorqi3"
  [(set (match_operand:QI 0 "bit_operand" "=r,U")
	(xor:QI (match_operand:QI 1 "bit_operand" "%0,0")
		(match_operand:QI 2 "nonmemory_operand" "rn,O")))]
  ""
  "
{
  if (fix_bit_operand (operands, 'O', XOR))
    DONE;
}")

(define_insn "xorhi3"
  [(set (match_operand:HI 0 "register_operand" "=r")
	(xor:HI (match_operand:HI 1 "general_operand" "%0")
		(match_operand:HI 2 "nonmemory_operand" "rn")))]
  ""
  "*
{
  if (TARGET_H8300)
    return \"xor	%s2,%s0\;xor	%t2,%t0\";
  else
    return \"xor	%S2,%S0\";
}"
  [(set_attr "type" "multi")
   (set_attr "length" "4")
   (set_attr "cc" "clobber")])

;; ??? There is an iorsi3 for TARGET_H8300.  Should we have xorsi3?

(define_insn "xorsi3"
  [(set (match_operand:SI 0 "register_operand" "=r,r")
	(xor:SI (match_operand:SI 1 "register_operand" "%0,0")
		(match_operand:SI 2 "nonmemory_operand" "r,i")))]
  "TARGET_H8300H"
  "@
   xor	%S2,%S0
   xor	%S2,%S0"
  [(set_attr "type" "arith")
   (set_attr "length" "4,6")
   (set_attr "cc" "clobber")])

;; ----------------------------------------------------------------------
;; NEGATION INSTRUCTIONS
;; ----------------------------------------------------------------------

(define_insn "negqi2"
  [(set (match_operand:QI 0 "register_operand" "=r")
	(neg:QI (match_operand:QI 1 "general_operand" "0")))]
  ""
  "neg	%X0"
  [(set_attr "type" "arith")
   (set_attr "length" "2")
   (set_attr "cc" "clobber")])

(define_expand "neghi2"
  [(set (match_operand:HI 0 "register_operand" "=r")
	(neg:HI (match_operand:HI 1 "general_operand" "0")))]
  ""
  "
{
  if (TARGET_H8300)
    {
      emit_insn (gen_neghi2_h8300 (operands[0], operands[1]));
      DONE;
    }
}")

(define_expand "neghi2_h8300"
  [(set (match_dup 2)
	(not:HI (match_operand:HI 1 "register_operand" "r")))
   (set (match_dup 2) (plus:HI (match_dup 2) (const_int 1)))
   (set (match_operand:HI 0 "register_operand" "=r")
	(match_dup 2))]
  ""
  "{ operands[2] = gen_reg_rtx (HImode); }")

(define_insn "neghi2_h8300h"
  [(set (match_operand:HI 0 "register_operand" "=r")
	(neg:HI (match_operand:HI 1 "general_operand" "0")))]
  "TARGET_H8300H"
  "neg	%T0"
  [(set_attr "type" "arith")
   (set_attr "length" "2")
   (set_attr "cc" "clobber")])

(define_expand "negsi2"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(neg:SI (match_operand:SI 1 "general_operand" "0")))]
  ""
  "
{
  if (TARGET_H8300)
    {
      emit_insn (gen_negsi2_h8300 (operands[0], operands[1]));
      DONE;
    }
}")

(define_expand "negsi2_h8300"
  [(set (match_dup 2)
	(not:SI (match_operand:SI 1 "register_operand" "r")))
   (set (match_dup 2) (plus:SI (match_dup 2) (const_int 1)))
   (set (match_operand:SI 0 "register_operand" "=r")
	(match_dup 2))]
  ""
  "{ operands[2] = gen_reg_rtx(SImode); }")

(define_insn "negsi2_h8300h"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(neg:SI (match_operand:SI 1 "general_operand" "0")))]
  "TARGET_H8300H"
  "neg	%S0"
  [(set_attr "type" "arith")
   (set_attr "length" "2")
   (set_attr "cc" "clobber")])

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

(define_insn "one_cmplqi2"
  [(set (match_operand:QI 0 "register_operand" "=r")
	(not:QI (match_operand:QI 1 "general_operand" "0")))]
  ""
  "not	%X0"
  [(set_attr "type" "arith")
   (set_attr "length" "2")
   (set_attr "cc" "set")])

(define_insn "one_cmplhi2"
  [(set (match_operand:HI 0 "register_operand" "=r")
	(not:HI (match_operand:HI 1 "general_operand" "0")))]
  ""
  "*
{
  if (TARGET_H8300)
    return \"not	%s0\;not	%t0\";
  else
    return \"not	%T0\";
}"
  [(set_attr "type" "arith")
   (set_attr "length" "4")
   (set_attr "cc" "clobber")])

(define_insn "one_cmplsi2"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(not:SI (match_operand:SI 1 "general_operand" "0")))]
  ""
  "*
{
  if (TARGET_H8300)
    return \"not	%w0\;not	%x0\;not	%y0\;not	%z0\";
  else
    return \"not	%S0\";
}"
  [(set_attr "type" "arith")
;; ??? length is wrong for 300h
   (set_attr "length" "8")
   (set_attr "cc" "clobber")])

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

;; Conditional jump instructions

(define_expand "ble"
  [(set (pc)
	(if_then_else (le (cc0)
			  (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "")

(define_expand "bleu"
  [(set (pc)
	(if_then_else (leu (cc0)
			   (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "")

(define_expand "bge"
  [(set (pc)
	(if_then_else (ge (cc0)
			  (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "")

(define_expand "bgeu"
  [(set (pc)
	(if_then_else (geu (cc0)
			   (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "")

(define_expand "blt"
  [(set (pc)
	(if_then_else (lt (cc0)
			  (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "")

(define_expand "bltu"
  [(set (pc)
	(if_then_else (ltu (cc0)
			   (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "")

(define_expand "bgt"
  [(set (pc)
	(if_then_else (gt (cc0)
			  (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "")

(define_expand "bgtu"
  [(set (pc)
	(if_then_else (gtu (cc0)
			   (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "")

(define_expand "beq"
  [(set (pc)
	(if_then_else (eq (cc0)
			  (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "")

(define_expand "bne"
  [(set (pc)
	(if_then_else (ne (cc0)
			  (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "")

(define_insn "branch_true"
  [(set (pc)
	(if_then_else (match_operator 1 "comparison_operator"
				      [(cc0) (const_int 0)])
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "*
{
  if (get_attr_length (insn) == 2) 
    return \"b%j1	%l0\";
  else if (get_attr_length (insn) == 4) 
    return \"b%j1	%l0:16\";
  else
    return \"b%k1	%L0\;jmp	@%l0\;%L0:\";
}" 
 [(set_attr "type" "branch")
   (set_attr "cc" "none")])

(define_insn "branch_false"
  [(set (pc)
	(if_then_else (match_operator 1 "comparison_operator"
				      [(cc0) (const_int 0)])
		      (pc)
		      (label_ref (match_operand 0 "" ""))))]
  ""
;; ??? We don't take advantage of 16 bit relative jumps in the 300h.
  "*
{
  if (get_attr_length (insn) == 2) 
    return \"b%k1	%l0\";
  else if (get_attr_length (insn) == 4) 
    return \"b%k1	%l0:16\";
  else
    return \"b%j1	%L0\;jmp	@%l0\;%L0:\";
}"
  [(set_attr "type" "branch")
   (set_attr "cc" "none")])

;; Unconditional and other jump instructions.

(define_insn "jump"
  [(set (pc)
	(label_ref (match_operand 0 "" "")))]
  ""
  "*
{
  if (get_attr_length (insn) == 2)
    return \"bra	%l0\";
  else if (get_attr_length (insn) == 4)
    return \"bra	%l0:16\";
  else
    return \"jmp	@%l0\";
}"
  [(set_attr "type" "branch")
   (set_attr "cc" "none")])

;; This is a define expand, because pointers may be either 16 or 32 bits.

(define_expand "tablejump"
  [(parallel [(set (pc) (match_operand 0 "register_operand" "r"))
	      (use (label_ref (match_operand 1 "" "")))])]
  ""
  "")

(define_insn "tablejump_h8300"
  [(set (pc) (match_operand:HI 0 "register_operand" "r"))
   (use (label_ref (match_operand 1 "" "")))]
  "TARGET_H8300"
  "jmp	@%0"
  [(set_attr "type" "branch")
   (set_attr "cc" "none")
   (set_attr "length" "2")])

(define_insn "tablejump_h8300h"
  [(set (pc) (match_operand:SI 0 "register_operand" "r"))
   (use (label_ref (match_operand 1 "" "")))]
  "TARGET_H8300H"
  "jmp	@%0"
  [(set_attr "type" "branch")
   (set_attr "cc" "none")
   (set_attr "length" "2")])

;; This is a define expand, because pointers may be either 16 or 32 bits.

;(define_insn "indirect_jump"
;  [(set (pc) (match_operand:HI 0 "register_operand" "r"))]
;  ""
;  "jmp	@%0"
;  [(set_attr "type" "branch")
;   (set_attr "cc" "none")
;   (set_attr "length" "2")])

(define_expand "indirect_jump"
  [(set (pc) (match_operand 0 "jump_address_operand" "Vr"))]
  ""
  "")

(define_insn "indirect_jump_h8300"
  [(set (pc) (match_operand:HI 0 "jump_address_operand" "V,r"))]
  "TARGET_H8300"
  "@
   jmp	@%0
   jmp	@%0"
  [(set_attr "type" "branch")
   (set_attr "cc" "none")
   (set_attr "length" "2")])

(define_insn "indirect_jump_h8300h"
  [(set (pc) (match_operand:SI 0 "jump_address_operand" "V,r"))]
  "TARGET_H8300H"
  "@
   jmp @%0
   jmp @%0"
  [(set_attr "type" "branch")
   (set_attr "cc" "none")
   (set_attr "length" "2")])

;; Call subroutine with no return value.

;; ??? Even though we use HImode here, this works for the 300h.

(define_insn "call"
  [(call (match_operand:QI 0 "call_insn_operand" "or")
	 (match_operand:HI 1 "general_operand" "g"))]
  ""
  "jsr	%0"
  [(set_attr "type" "call")
   (set_attr "cc" "clobber")
   (set_attr "length" "4")])

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

;; ??? Even though we use HImode here, this works on the 300h.

(define_insn "call_value"
  [(set (match_operand 0 "" "=r")
	(call (match_operand:QI 1 "call_insn_operand" "or")
	      (match_operand:HI 2 "general_operand" "g")))]
  ""
  "jsr	%1"
  [(set_attr "type" "call")
   (set_attr "cc" "clobber")
   (set_attr "length" "4")])

(define_insn "nop"
  [(const_int 0)]
  ""
  "nop"
  [(set_attr "type" "multi")
   (set_attr "cc" "none")
   (set_attr "length" "2")])

;; ----------------------------------------------------------------------
;; EXTEND INSTRUCTIONS
;; ----------------------------------------------------------------------

(define_insn "zero_extendqihi2"
  [(set (match_operand:HI 0 "register_operand" "=r,r")
	(zero_extend:HI (match_operand:QI 1 "general_operand" "0,g")))]
  ""
  "*
{
  if (which_alternative==0)
    return \"mov.b	#0,%t0\";

  if (TARGET_H8300)
    return \"mov.b	%X1,%s0\;mov.b	#0,%t0\";
  else
    {
      /* ??? See how often this gets optimized.  */
      if (REG_P (operands[1]) && (REGNO (operands[1]) == REGNO (operands[0])))
	return \"extu.w	%T0\";
      else
	return \"mov.b	%X1,%s0\;extu.w	%T0\";
    }
}"
  [(set_attr "type" "multi")
;; ??? This length is wrong for one case.
   (set_attr "length" "4")
   (set_attr "cc" "clobber")])

(define_insn "zero_extendhisi2"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(zero_extend:SI (match_operand:HI 1 "general_operand" "g")))]
  "TARGET_H8300H"
  "*
{
  /* ??? See how often this gets optimized.  */
  if (REG_P (operands[1]) && (REGNO (operands[1]) == REGNO (operands[0])))
    return \"extu.l	%S0\";
  else
    return \"mov.w	%T1,%T0\;extu.l	%S0\";
}"
  [(set_attr "type" "multi")
;; ??? This length is wrong for one case.
   (set_attr "length" "4")
   (set_attr "cc" "clobber")])

(define_insn "extendqihi2"
  [(set (match_operand:HI 0 "register_operand" "=r")
	(sign_extend:HI (match_operand:QI 1 "general_operand" "g")))]
  ""
  "*
{
  if (TARGET_H8300)
    {
      /* ??? See how often this gets optimized.  */
      if (REG_P (operands[1]) && (REGNO (operands[1]) == REGNO (operands[0])))
	return \"bld	#7,%s0\;subx	%t0,%t0\";
      else
	return \"mov.b	%X1,%s0\;bld	#7,%s0\;subx	%t0,%t0\";
    }
  else
    {
      /* ??? See how often this gets optimized.  */
      if (REG_P (operands[1]) && (REGNO (operands[1]) == REGNO (operands[0])))
	return \"exts.w	%T0\";
      else
	return \"mov.b	%X1,%s0\;exts.w	%T0\";
    }
}"
  [(set_attr "type" "multi")
;; ??? Length is wrong in some cases.
   (set_attr "length" "6")
   (set_attr "cc" "clobber")])

(define_expand "extendhisi2"
  [(set (match_operand:SI 0 "register_operand" "")
	(sign_extend:SI (match_operand:HI 1 "general_operand" "")))]
  ""
  "
{
  if (TARGET_H8300)
    emit_insn (gen_extendhisi2_h8300 (operands[0], operands[1]));
  else
    emit_insn (gen_extendhisi2_h8300h (operands[0], operands[1]));
  DONE;
}")

(define_expand "extendhisi2_h8300"
  [(set (reg:HI 1) (match_operand:HI 1 "general_operand" ""))
   (set (reg:SI 0) (sign_extend:SI (reg:HI 1)))
   (set (match_operand:SI 0 "general_operand" "" ) (reg:SI 0))]
  "TARGET_H8300"
  "")

(define_expand "extendhisi2_h8300h"
  [(set (match_operand:SI 0 "register_operand" "")
	(sign_extend:SI (match_operand:HI 1 "general_operand" "")))]
  "TARGET_H8300H"
  "")

(define_insn "extendhisi2_h8300_internal"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(sign_extend:SI (match_operand:HI 1 "register_operand" "0")))]
  "TARGET_H8300"
  "mov.w	%T1,%f0\;bld	#7,%x0\;subx	%y0,%y0\;subx	%z0,%z0"
  [(set_attr "length" "10")
   (set_attr "cc" "clobber")])

(define_insn "extendhisi2_h8300h_internal"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(sign_extend:SI (match_operand:HI 1 "general_operand" "g")))]
  "TARGET_H8300H"
  "*
{
  /* ??? See how often this gets optimized.  */
  if (REG_P (operands[1]) && (REGNO (operands[1]) == REGNO (operands[0])))
    return \"exts.l	%S0\";
  else
    return \"mov.w	%T1,%T0\;exts.l	%S0\";
}"
  [(set_attr "length" "10")
   (set_attr "cc" "clobber")])

;; ----------------------------------------------------------------------
;; SHIFTS
;; ----------------------------------------------------------------------
;;
;; We make some attempt to provide real efficient shifting.  One example is
;; doing an 8 bit shift of a 16 bit value by moving a byte reg into the other
;; reg and moving 0 into the former reg.
;;
;; We also try to achieve this in a uniform way.  IE: We don't try to achieve
;; this in both rtl and at insn emit time.  Ideally, we'd use rtl as that would
;; give the optimizer more cracks at the code.  However, we wish to do things
;; like optimizing shifting the sign bit to bit 0 by rotating the other way.
;; There is rtl to handle this (rotate + and), but the h8/300 doesn't handle
;; 16 bit rotates.  Also, if we emit complicated rtl, combine may not be able
;; to detect cases it can optimize.
;;
;; For these and other fuzzy reasons, I've decided to go the less pretty but
;; easier "do it at insn emit time" route.

;; QI BIT SHIFTS

(define_expand "ashlqi3"
  [(set (match_operand:QI 0 "register_operand" "")
	(ashift:QI (match_operand:QI 1 "register_operand" "")
		   (match_operand:QI 2 "nonmemory_operand" "")))]
  ""
  "if (expand_a_shift (QImode, ASHIFT, operands)) DONE;else FAIL;")

(define_expand "ashrqi3"
  [(set (match_operand:QI 0 "register_operand" "")
	(ashiftrt:QI (match_operand:QI 1 "register_operand" "")
		     (match_operand:QI 2 "nonmemory_operand" "")))]
  ""
  "if (expand_a_shift (QImode, ASHIFTRT, operands)) DONE;else FAIL;")

(define_expand "lshrqi3"
  [(set (match_operand:QI 0 "register_operand" "")
	(lshiftrt:QI (match_operand:QI 1 "register_operand" "")
		     (match_operand:QI 2 "nonmemory_operand" "")))]
  ""
  "if (expand_a_shift (QImode, LSHIFTRT, operands)) DONE;else FAIL;")

;; WARNING: The constraints on the scratch register say one is not needed
;; for constant shifts of 1,2,3,4.  Emit_a_shift() must know this.

(define_insn "shiftbyn_QI"
  [(set (match_operand:QI 0 "register_operand" "=r,r")
	(match_operator:QI 3 "nshift_operator" 
			[ (match_operand:QI 1 "register_operand" "0,0")
			  (match_operand:QI 2 "nonmemory_operand" "IKM,rn")]))
   (clobber (match_scratch:QI 4 "=X,&r"))]
  ""
  "* return emit_a_shift (insn, operands);"
  [(set_attr "type" "arith")
   (set_attr "length" "20")
;; ??? We'd like to indicate that cc is set here, and it is for simple shifts.
;; However, for cases that loop or are done in pieces, cc does not contain
;; what we want.  Emit_a_shift is free to tweak cc_status as desired.
   (set_attr "cc" "clobber")])

;; HI BIT SHIFTS

(define_expand "ashlhi3"
  [(set (match_operand:HI 0 "register_operand" "")
	(ashift:HI (match_operand:HI 1 "nonmemory_operand" "")
		   (match_operand:QI 2 "nonmemory_operand" "")))]
  ""
  "if (expand_a_shift (HImode, ASHIFT, operands)) DONE;else FAIL;")

(define_expand "lshrhi3"
  [(set (match_operand:HI 0 "register_operand" "")
	(lshiftrt:HI (match_operand:HI 1 "general_operand_src" "")
		     (match_operand:QI 2 "nonmemory_operand" "")))]
  ""
  "if (expand_a_shift (HImode, LSHIFTRT, operands)) DONE;else FAIL;")

(define_expand "ashrhi3"
  [(set (match_operand:HI 0 "register_operand" "")
	(ashiftrt:HI (match_operand:HI 1 "register_operand" "")
		     (match_operand:QI 2 "nonmemory_operand" "")))]
  ""
  "if (expand_a_shift (HImode, ASHIFTRT, operands)) DONE;else FAIL;")

;; WARNING: The constraints on the scratch register say one is not needed
;; for constant shifts of 1,2,3,4.  Emit_a_shift() must know this.

(define_insn "shiftbyn_HI"
  [(set (match_operand:HI 0 "register_operand" "=r,r")
	(match_operator:HI 3 "nshift_operator" 
			[ (match_operand:HI 1 "register_operand" "0,0")
			  (match_operand:QI 2 "nonmemory_operand" "IKM,rn")]))
   (clobber (match_scratch:QI 4 "=X,&r"))]
  ""
  "* return emit_a_shift (insn, operands);"
  [(set_attr "type" "arith")
   (set_attr "length" "20")
;; ??? We'd like to indicate that cc is set here, and it is for simple shifts.
;; However, for cases that loop or are done in pieces, cc does not contain
;; what we want.  Emit_a_shift is free to tweak cc_status as desired.
   (set_attr "cc" "clobber")])

;;  SI BIT SHIFTS

(define_expand "ashlsi3"
  [(set (match_operand:SI 0 "register_operand" "")
	(ashift:SI
	 (match_operand:SI 1 "general_operand_src" "")
	 (match_operand:QI 2 "nonmemory_operand" "")))]
  ""
  "if (expand_a_shift (SImode, ASHIFT, operands)) DONE;else FAIL;")

(define_expand "lshrsi3"
  [(set (match_operand:SI 0 "register_operand" "")
	(lshiftrt:SI
	 (match_operand:SI 1 "general_operand_src" "")
	 (match_operand:QI 2 "nonmemory_operand" "")))]
  ""
  "if (expand_a_shift (SImode, LSHIFTRT, operands)) DONE;else FAIL;")

(define_expand "ashrsi3"
  [(set (match_operand:SI 0 "register_operand" "")
	(ashiftrt:SI
	 (match_operand:SI 1 "general_operand_src" "")
	 (match_operand:QI 2 "nonmemory_operand" "")))]
  ""
  "if (expand_a_shift (SImode, ASHIFTRT, operands)) DONE;else FAIL;")

;; WARNING: The constraints on the scratch register say one is not needed
;; for constant shifts of 1,2.  Emit_a_shift() must know this.

(define_insn "shiftbyn_SI"
  [(set (match_operand:SI 0 "register_operand" "=r,r")
	(match_operator:SI 3 "nshift_operator" 
			[ (match_operand:SI 1 "register_operand" "0,0")
			  (match_operand:QI 2 "nonmemory_operand" "IK,rn")]))
   (clobber (match_scratch:QI 4 "=X,&r"))]
  ""
  "* return emit_a_shift (insn, operands);"
  [(set_attr "type" "arith")
   (set_attr "length" "20")
;; ??? We'd like to indicate that cc is set here, and it is for simple shifts.
;; However, for cases that loop or are done in pieces, cc does not contain
;; what we want.  Emit_a_shift is free to tweak cc_status as desired.
   (set_attr "cc" "clobber")])

;; -----------------------------------------------------------------
;; BIT FIELDS
;; -----------------------------------------------------------------
;; The H8/300 has given 1/8th of its opcode space to bitfield
;; instructions so let's use them as well as we can

;; BCC and BCS patterns.

(define_insn "bcs_qiqi"
  [(set (pc)
	(if_then_else 
	 (match_operator 1 "eq_operator"
			 [(zero_extract:QI (match_operand:QI 2 "bit_operand" "Ur")
					   (const_int 1)
					   (match_operand:HI 3 "immediate_operand" "i"))
			  (const_int 0)])
	 (label_ref (match_operand 0 "" ""))
	 (pc)))]
  ""
  "*
{
  output_asm_insn(\"bld	%Z3,%Y2\", operands);
  if (get_attr_length (insn) == 2) 
    return \"b%d1	%l0\";
  else if (get_attr_length (insn) == 4) 
    return \"b%d1	%l0:16\";
  else
    return \"b%g1	%L0\;jmp	@%l0\;%L0:\";
}" 
  [(set_attr "type" "branch")
   (set_attr "cc" "clobber")])

(define_insn "bcs_hihi"
  [(set (pc)
	(if_then_else 
	 (match_operator 1 "eq_operator"
			 [(zero_extract:HI (match_operand:HI 2 "bit_operand" "Ur")
					   (const_int 1)
					   (match_operand:HI 3 "immediate_operand" "i"))
			  (const_int 0)])
	 (label_ref (match_operand 0 "" ""))
	 (pc)))]
  ""
  "*
{
  output_asm_insn(\"bld	%Z3,%Y2\", operands);
  if (get_attr_length (insn) == 2) 
    return \"%d1	%l0\";
  else if (get_attr_length (insn) == 4) 
    return \"%d1	%l0:16\";
  else
    return \"%g1	%L0\;jmp	@%l0\;%L0:\";
}" 
  [(set_attr "type" "branch")
   (set_attr "cc" "clobber")])

(define_insn "bcs_hiqi"
  [(set (pc)
	(if_then_else 
	 (match_operator 1 "eq_operator"
			 [(zero_extract:HI (match_operand:QI 2 "bit_operand" "Ur")
					   (const_int 1)
					   (match_operand:HI 3 "immediate_operand" "i"))
			  (const_int 0)])
	 (label_ref (match_operand 0 "" ""))
	 (pc)))]
  ""
  "*
{
  output_asm_insn(\"bld	%Z3,%Y2\", operands);
  if (get_attr_length (insn) == 2) 
    return \"%d1	%l0\";
  else if (get_attr_length (insn) == 4) 
    return \"%d1	%l0:16\";
  else
    return \"%g1	%L0\;jmp	@%l0\;%L0:\";
}" 
  [(set_attr "type" "branch")
   (set_attr "cc" "clobber")])

;; BLD and BST patterns

(define_insn "extract_1"
  [(set (match_operand:HI 0 "register_operand" "=&r")
	(zero_extract:HI (match_operand:QI 1 "bit_operand" "Ur")
			 (const_int 1)
			 (match_operand:HI 2 "immediate_operand" "i")))]
  ""
  "sub.w	%0,%0\;bld	%Z2,%Y1\;bst	#0,%X0")

(define_insn "extract_1_hi"
  [(set (match_operand:HI 0 "register_operand" "=&r")
	(zero_extract:HI (match_operand:HI 1 "bit_operand" "Ur")
			 (const_int 1)
			 (match_operand:HI 2 "immediate_operand" "i")))]
  ""
  "sub.w	%0,%0\;bld	%Z2,%Y1\;bst	#0,%X0")

(define_insn "insert_1"
  [(set (zero_extract:HI (match_operand:QI 0 "bit_operand" "+Ur")
			 (const_int 1)
			 (match_operand:HI 1 "immediate_operand" "i"))
	(zero_extract:HI (match_operand:QI 2 "bit_operand" "Ur")
			 (const_int 1)
			 (const_int 0)))]
  ""
  "bld	#0,%X2\;bst	%Z1,%Y0 ; i1")

;; This is how combine canonicalizes this pattern.  This is perhaps a bug
;; in combine.c, but there is no problem with writing it this way so we do.
(define_insn "extract_insert_1"
  [(set (zero_extract:QI (match_operand:QI 0 "bit_operand" "+Ur")
			 (const_int 1)
			 (match_operand:HI 1 "immediate_operand" "i"))
	(lshiftrt:QI (match_operand:QI 2 "bit_operand" "Ur")
		     (match_operand:HI 3 "immediate_operand" "i")))]
 ""
 "bld	%Z3,%Y2\;bst	%Z1,%Y0; ei1")

;; BAND, BOR, and BXOR patterns

(define_insn "bitlogical_1"
  [(set (match_operand:HI 0 "bit_operand" "=Ur")
	(match_operator:HI 4 "bit_operator"
	   [(zero_extract:HI (match_operand:QI 1 "bit_operand" "Ur")
			     (const_int 1)
			     (match_operand:HI 2 "immediate_operand" "i"))
	    (match_operand:HI 3 "bit_operand" "0")]))]
  ""
  "bld	%Z2,%Y1\;%b4	#0,%X0\;bst	#0,%X0; bl1")

(define_insn "bitlogical_1_hi"
  [(set (match_operand:HI 0 "bit_operand" "=Ur")
	(match_operator:HI 4 "bit_operator"
	   [(zero_extract:HI (match_operand:HI 1 "bit_operand" "Ur")
			     (const_int 1)
			     (match_operand:HI 2 "immediate_operand" "i"))
	    (match_operand:HI 3 "bit_operand" "0")]))]
  ""
  "bld	%Z2,%Y1\;%b4	#0,%X0\;bst	#0,%X0; bl2")

(define_insn "bitlogical_2"
  [(set (match_operand:HI 0 "bit_operand" "=Ur")
	(match_operator:HI 5 "bit_operator"
	   [(zero_extract:HI (match_operand:QI 1 "bit_operand" "Ur")
			     (const_int 1)
			     (match_operand:HI 2 "immediate_operand" "i"))
	    (zero_extract:HI (match_operand:QI 3 "bit_operand" "Ur")
			     (const_int 1)
			     (match_operand:HI 4 "immediate_operand" "i"))]))]
  ""
  "bld	%Z2,%Y1\;%b5	%Z4,%Y3\;bst	#0,%X0; bl3")

(define_insn "bitlogical_2_hi"
  [(set (match_operand:HI 0 "bit_operand" "=Ur")
	(match_operator:HI 5 "bit_operator"
	   [(zero_extract:HI (match_operand:HI 1 "bit_operand" "Ur")
			     (const_int 1)
			     (match_operand:HI 2 "immediate_operand" "i"))
	    (zero_extract:HI (match_operand:HI 3 "bit_operand" "Ur")
			     (const_int 1)
			     (match_operand:HI 4 "immediate_operand" "i"))]))]
  ""
  "bld	%Z2,%Y1\;%b5	%Z4,%Y3\;bst	#0,%X0; bl3")

;; This is how combine canonicalizes this pattern.  This is perhaps a bug
;; in combine.c, but there is no problem with writing it this way so we do.
(define_insn "bitlogical_3"
  [(set (zero_extract:QI (match_operand:QI 0 "bit_operand" "+Ur")
			 (const_int 1)
			 (match_operand:HI 1 "immediate_operand" "i"))
	(match_operator:QI 6 "bit_operator"
	   [(lshiftrt:QI (match_operand:QI 2 "bit_operand" "Ur")
			 (match_operand:HI 3 "immediate_operand" "i"))
	    (lshiftrt:QI (match_operand:QI 4 "bit_operand" "Ur")
			 (match_operand:HI 5 "immediate_operand" "i"))]))]
  ""
  "bld	%Z3,%Y2\;%b6	%Z5,%Y4\;bst	%Z1,%Y0; bl5")
						     
;; This is how combine canonicalizes this pattern.  This is perhaps a bug
;; in combine.c, but there is no problem with writing it this way so we do.
(define_insn "bitnot_1"
  [(set (zero_extract:QI (match_operand:QI 0 "bit_operand" "=Ur")
			 (const_int 1)
			 (match_operand:HI 1 "immediate_operand" "i"))
	(lshiftrt:QI (xor:QI (match_operand:QI 2 "bit_operand" "0")
			     (match_operand:HI 3 "immediate_operand" "i"))
		     (match_operand:HI 4 "immediate_operand" "1")))]
  "GET_CODE (operands[3]) == CONST_INT && GET_CODE (operands[1]) == CONST_INT
   && exact_log2 (INTVAL (operands[3])) == INTVAL (operands[1])"
  "bnot	%Z1,%Y0")

;; ??? Implement BIAND, BIOR, BIXOR

;; ??? Implement BILD, BIST

;; ??? Apparently general_operand for the 1st and 2nd operands is useful,
;; but I don't know why.  --Jim

(define_expand "insv"
  [(set (zero_extract:HI (match_operand:QI 0 "bit_operand" "Ur")
			 (match_operand:HI 1 "general_operand" "g")
			 (match_operand:HI 2 "general_operand" "g"))
	(zero_extract:HI (match_operand:QI 3 "bit_operand" "Ur")
			 (const_int 1)
			 (const_int 0)))]
;; ??? This should have word mode which is SImode for the h8/300h.
  "TARGET_H8300"
  "
{
  if (INTVAL (operands[1]) != 1)
    FAIL;

  /* ??? HACK ???
     This INSV pattern is wrong.  It should use HImode for operand 3.
     Also, the zero_extract around operand 3 is superfluous and should be
     deleted.  Fixing this is more work than we care to do for the moment,
     because it means most of the above patterns would need to be rewritten,
     and we also need more combine.c patches to make this work.

     So, for now, we work around this bug by simply not accepting any bitfield
     inserts that have a position greater than fits in QImode.  */

  if (GET_CODE (operands[2]) != CONST_INT || INTVAL (operands[2]) >= 8)
    FAIL;

  /* The bit_operand predicate accepts any memory during RTL generation, but
     only 'U' memory afterwards, so if this is a MEM operand, we must force
     it to be valid for 'U' by reloading the address.  */

  if (GET_CODE (operands[0]) == MEM && ! EXTRA_CONSTRAINT (operands[0], 'U'))
    {
      rtx mem;
      mem = gen_rtx (MEM, GET_MODE (operands[0]),
		     copy_to_mode_reg (Pmode, XEXP (operands[0], 0)));
      RTX_UNCHANGING_P (mem) = RTX_UNCHANGING_P (operands[0]);
      MEM_IN_STRUCT_P (mem) = MEM_IN_STRUCT_P (operands[0]);
      MEM_VOLATILE_P (mem) = MEM_VOLATILE_P (operands[0]);
      operands[0] = mem;
    }

  /* Likewise for operands[3].  */

  if (GET_CODE (operands[3]) == MEM && ! EXTRA_CONSTRAINT (operands[3], 'U'))
    {
      rtx mem;
      mem = gen_rtx (MEM, GET_MODE (operands[3]),
		     copy_to_mode_reg (Pmode, XEXP (operands[3], 0)));
      RTX_UNCHANGING_P (mem) = RTX_UNCHANGING_P (operands[3]);
      MEM_IN_STRUCT_P (mem) = MEM_IN_STRUCT_P (operands[3]);
      MEM_VOLATILE_P (mem) = MEM_VOLATILE_P (operands[3]);
      operands[3] = mem;
    }
}")

;; ??? Apparently general_operand for the 2nd and 3rd operands is useful,
;; but I don't know why.  --Jim

(define_expand "extzv"
  [(set (match_operand:HI 0 "register_operand" "") 
	(zero_extract:HI (match_operand:QI 1 "bit_operand" "")
			 (match_operand:HI 2 "general_operand" "g")
			 (match_operand:HI 3 "general_operand" "g")))]
;; ??? This should have word mode which is SImode for the h8/300h.
  "TARGET_H8300"
  "
{
  if (INTVAL (operands[2]) != 1)
    FAIL;

  /* The bit_operand predicate accepts any memory during RTL generation, but
     only 'U' memory afterwards, so if this is a MEM operand, we must force
     it to be valid for 'U' by reloading the address.  */

  if (GET_CODE (operands[1]) == MEM && ! EXTRA_CONSTRAINT (operands[1], 'U'))
    {
      rtx mem;
      mem = gen_rtx (MEM, GET_MODE (operands[1]),
		     copy_to_mode_reg (Pmode, XEXP (operands[1], 0)));
      RTX_UNCHANGING_P (mem) = RTX_UNCHANGING_P (operands[1]);
      MEM_IN_STRUCT_P (mem) = MEM_IN_STRUCT_P (operands[1]);
      MEM_VOLATILE_P (mem) = MEM_VOLATILE_P (operands[1]);
      operands[1] = mem;
    }
}")

;; -----------------------------------------------------------------
;; STACK POINTER MANIPULATIONS
;; -----------------------------------------------------------------

;; This pattern is needed because there is no way on the H8/300
;; to add a 16 bit immediate value to the stack pointer in one 
;; instruction, which could leave an invalid instruction if interrupted
;; half way through.  Here we add to the stack pointer from a
;; register.

(define_insn "stack_pointer_manip"
  [(set (match_operand:HI 0 "register_operand" "=&ra")
	(plus:HI (match_operand:HI 1 "general_operand_src" "g")
		 (match_operand:HI 2 "register_operand" "ra")))]
  "TARGET_H8300"
  "mov.w	%T1,%T0\;add.w	%T2,%T0"
  [(set_attr "type" "arith")
   (set_attr "length" "6")
   (set_attr "cc" "set")])


;; -------------------------------------------
;; BLK moves
;; -------------------------------------------

(define_expand "movstrhi"
  [(parallel [(set (mem:BLK (match_operand:BLK 0 "general_operand" ""))
		   (mem:BLK (match_operand:BLK 1 "general_operand" "")))
	     (use (match_operand:HI 2 "general_operand" ""))
	     (use (match_operand:HI 3 "immediate_operand" ""))
	     (clobber (match_dup 3))
  ])]
  ""
  "
{
	rtx src_ptr = copy_to_mode_reg (Pmode, XEXP(operands[1], 0));
	rtx dst_ptr = copy_to_mode_reg (Pmode, XEXP(operands[0], 0));
	
        int max = GET_CODE (operands[2]) == CONST_INT
	  ? MIN (INTVAL (operands[2]), INTVAL (operands[3])) : 1;
	enum machine_mode mode = max >= 2 ? HImode : QImode;
	rtx tmpreg = gen_reg_rtx (mode);
	rtx increment = mode == QImode ? const1_rtx : const2_rtx;
	rtx length = operands[2];
	rtx label = gen_label_rtx ();
	rtx end_src_ptr = gen_reg_rtx (Pmode);

/*	emit_move_insn (length, gen_rtx(MINUS, HImode, length, increment));*/
	FAIL;
	if (Pmode == HImode)
	  emit_insn (gen_addhi3 (end_src_ptr, src_ptr, length));
	else
	  emit_insn (gen_addsi3 (end_src_ptr, src_ptr, length));

	emit_label (label);
	emit_move_insn (tmpreg, gen_rtx (MEM, mode, src_ptr));
	emit_move_insn (gen_rtx (MEM, mode, dst_ptr), tmpreg);
	emit_insn (gen_rtx (SET, VOIDmode, src_ptr,
			    gen_rtx (PLUS, Pmode, src_ptr, increment)));
	emit_insn (gen_rtx (SET, VOIDmode, dst_ptr,
			    gen_rtx (PLUS, Pmode, dst_ptr, increment)));

	emit_insn (gen_rtx (SET, VOIDmode, cc0_rtx,
			    gen_rtx (COMPARE, Pmode, src_ptr, end_src_ptr)));
	emit_jump_insn (gen_bne (label));

	DONE;	
}")

;; ----------------------------------------------
;; Peepholes go at the end.
;; ----------------------------------------------

;; Notice when two byte moves in a row could be a word move.

(define_peephole
  [(set (match_operand:QI 0 "register_operand" "=r")
	(mem:QI (plus:HI (match_operand:HI 1 "register_operand" "ra")
			 (match_operand:HI 2 "immediate_operand" "n"))))
   (set (match_operand:QI 3 "register_operand" "=r")
	(mem:QI (plus:HI (match_dup 1)
			 (match_operand:HI 4 "immediate_operand" "n"))))]
  "(INTVAL(operands[2]) == INTVAL(operands[4])+1) && REGNO(operands[0]) +1 == REGNO(operands[3])"
  "mov.w	@(%u4,%T1),%T0"
  [(set_attr "length" "6")
   (set_attr "cc" "set")])

(define_peephole
  [(set (mem:QI (plus:HI (match_operand:HI 1 "register_operand" "ra")
			 (match_operand:HI 2 "immediate_operand" "n")))
	(match_operand:QI 0 "register_operand" "r"))
   (set (mem:QI (plus:HI (match_dup 1)
			 (match_operand:HI 4 "immediate_operand" "n")))
	(match_operand:QI 3 "register_operand" "r"))]
  "(INTVAL(operands[2]) == INTVAL(operands[4])+1) && REGNO(operands[0]) +1 == REGNO(operands[3])"
  "mov.w	%T0,@(%u4,%T1)"
  [(set_attr "length" "6")
   (set_attr "cc" "set")])

;; Notice a move which could be post incremented.

(define_peephole 
  [(set (match_operand:QI 0 "register_operand" "")
	(mem:QI (match_operand:HI 1 "register_operand" "")))
   (set (match_dup 1) (plus:HI (match_dup 1) (const_int 1)))]
  "REGNO(operands[1]) != REGNO(operands[0])"
  "mov.b	@%T1+,%X0"
  [(set_attr "length" "2")
   (set_attr "cc" "set")])

(define_peephole 
  [(set (match_operand:HI 0 "register_operand" "")
	(mem:HI (match_operand:HI 1 "register_operand" "")))
   (set (match_dup 1) (plus:HI (match_dup 1) (const_int 2)))]
  "REGNO(operands[1]) != REGNO(operands[0])"
  "mov.w	@%T1+,%T0"
  [(set_attr "length" "2")
   (set_attr "cc" "set")])

;; Notice a move which could be predecremented.

(define_peephole 
  [(set (match_operand:HI 1 "register_operand" "")
	(plus:HI (match_dup 1) (const_int -1)))
   (set (mem:QI (match_dup 1))
		(match_operand:QI 0 "register_operand" ""))]
  "REGNO(operands[1]) != REGNO(operands[0])"
  "mov.b	%X0,@-%T1"
  [(set_attr "length" "2")
   (set_attr "cc" "set")])

(define_peephole 
  [(set (match_operand:HI 1 "register_operand" "")
	(plus:HI (match_dup 1) (const_int -1)))
   (set (mem:HI (match_dup 1))
		(match_operand:HI 0 "register_operand" ""))]
  "REGNO(operands[1]) != REGNO(operands[0])"
  "mov.w	%T0,@-%T1"
  [(set_attr "length" "2")
   (set_attr "cc" "set")])

;(define_insn ""
;  [(set (match_operand:HI 0 "register_operand" "=r")
;	(MEM:HI (match_operand:HI 1 "register_operand" "r")))
;   (set (match_operand:HI 3 "register_operand" "=r")
;	(zero_extract:HI (match_dup 0)
;			 (const_int 1)
;			 (match_operand:HI 2 "general_operand" "g")))
;   (set (MEM:HI (match_dup 1) (match_dup 3)))]
;  ""
;  "bld	#0,%3l\;bst	%Z2,%0%Y1"
;  [(set_attr "type" "multi")
;   (set_attr "length" "4")
;   (set_attr "cc" "clobber")])

(define_insn "fancybset1"
  [(set (match_operand:QI 0 "bit_operand" "=Ur")
	(ior:QI (subreg:QI 
		 (ashift:HI (const_int 1)
			    (subreg:QI (match_operand:HI 1 "register_operand" "ri") 0)) 0)
		(match_dup 0)))]
  ""
  "bset	%X1,%X0")	

(define_insn "fancybset"
  [(set (match_operand:QI 0 "bit_operand" "=Ur")
	(ior:QI (subreg:QI 
		 (ashift:HI (const_int 1)
			    (match_operand:HI 1 "nonmemory_operand" "ri") ) 0)
		(match_operand:QI 2 "general_operand" "Ur")))]
  ""
  "mov.b	%X2,%X0\;bset	%X1,%X0")	


(define_insn "fancybclr4"
  [(set (match_operand:QI 0 "general_operand" "=Ur,Ur")
	(and:QI 
	 (subreg:QI 
	  (rotate:HI (const_int -2)
		     (match_operand:HI 2 "nonmemory_operand" "ri,ri") ) 0)
	 (match_operand:QI 1 "general_operand" "0,Ur")))
   (clobber (match_scratch:HI 3 "=X,&r"))]
  ""
  "@
   bclr	%X2,%X0; l1
   mov.b	%X1,%X3\;mov.b	%3,%0\;bclr	%X2,%X0; l3")

(define_insn "fancybclr5"
  [(set (match_operand:QI 0 "general_operand" "=Ur,Ur")
	(and:QI 
	 (subreg:QI 
	  (rotate:HI (const_int -2)
		     (match_operand:QI 2 "nonmemory_operand" "ri,ri")) 0)
	 (match_operand:QI 1 "general_operand" "0,Ur")))
   (clobber (match_scratch:HI 3 "=X,&r"))]
  ""
  "@
   bclr	%X2,%X0; l1
   mov.b	%X1,%X3\;mov.b	%3,%0\;bclr	%X2,%X0;l2")

(define_insn "fancybclr2"
  [(set (match_operand:QI 0 "general_operand" "=U,r")
	(and:QI 
	 (subreg:QI 
	  (rotate:HI (const_int -2)
		     (match_operand:HI 2 "nonmemory_operand" "ri,ri") ) 0)
	 (match_operand:QI 1 "general_operand" "0,0")))]
  ""
  "bclr	%X2,%X0")

(define_insn "fancybclr3"
  [(set (match_operand:QI 0 "general_operand" "=U,r")
	(and:QI 
	 (subreg:QI 
	  (rotate:HI (const_int -2)
		     (match_operand:QI 2 "nonmemory_operand" "ri,ri")) 0)
	 (match_operand:QI 1 "general_operand" "0,0")))]
  ""
  "bclr	%X2,%X0")

(define_insn "fancybclr"
  [(set (match_operand:QI 0 "general_operand" "=r")
	(and:QI (not:QI (match_operand:QI 1 "general_operand" "0"))
		(match_operand:QI 2 "general_operand" "r")))]
  ""
  "not	%X0\;and	%X2,%X0")

(define_insn "fancybsetp3"
  [(set (match_operand:QI 0 "bit_operand" "=Ur")
	(ior:QI (subreg:QI (ashift:HI (const_int 1)
				      (match_operand:QI 1 "register_operand" "r")) 0)
		(match_operand:QI 2 "bit_operand" "0")))]
  ""
  "bset	%X1,%X0")

(define_insn "fancybsetp2"
  [(set (match_operand:QI 0 "general_operand" "=r,U")
	(ior:QI (subreg:QI (ashift:HI (const_int 1)
				      (match_operand:QI 1 "register_operand" "r,r")) 0)
		(match_operand:QI 2 "general_operand" "U,r")))]
  ""
  "mov.b	%X2,%X0\;bset	%X1,%X0")
	
(define_insn "fancybnot"
  [(set (match_operand:QI 0 "bit_operand" "=Ur")
	(xor:QI (subreg:QI (ashift:HI (const_int 1)
				      (match_operand:QI 1 "register_operand" "r")) 0)
		(match_operand:QI 2 "bit_operand" "0")))]

  ""
  "bnot	%X1,%X0")

(define_insn "fancy_btst"
  [(set (pc)
	(if_then_else (eq (zero_extract:HI (zero_extend:HI (match_operand:QI 1 "general_operand" "Ur"))
					   (const_int 1)
					   (match_operand:HI 2 "nonmemory_operand" "rn"))
			  (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "*
{
  if (get_attr_length (insn) == 2)
    return \"btst	%X2,%X1\;beq	%l0\";
  else if (get_attr_length (insn) == 4)
    return \"btst	%X2,%X1\;beq	%l0:16\";
  else
    return \"btst	%X2,%X1\;bne	%L1\;jmp	@%l0\;%L1:\";
}"
  [(set_attr "type" "branch")
   (set_attr "cc" "clobber")])

(define_insn "fancy_btst1"
  [(set (pc)
	(if_then_else (ne (zero_extract:HI (zero_extend:HI (match_operand:QI 1 "general_operand" "Ur"))
					   (const_int 1)
					   (match_operand:HI 2 "nonmemory_operand" "rn"))
			  (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "*
{
  if (get_attr_length (insn) == 2)
    return \"btst	%X2,%X1\;bne	%l0\";
  else if (get_attr_length (insn) == 4)
    return \"btst	%X2,%X1\;bne	%l0:16\";
  else
    return \"btst	%X2,%X1\;beq	%L1\;jmp	@%l0\;%L1:\";
}"
  [(set_attr "type" "branch")
   (set_attr "cc" "clobber")])

(define_insn "pxor"
  [(set (zero_extract:QI (match_operand:QI 0 "bit_operand" "=r,U")
			 (const_int 1)
			 (match_operand 1 "immediate_operand" "n,n"))
	(and:QI (not:QI (match_operand:QI 2 "bit_operand" "r,U"))
			(const_int 1)))]
  ""
  "bld	#0,%X2\;bist	%1,%0"
  [(set_attr "type" "arith")
   (set_attr "length" "4")
   (set_attr "cc" "clobber")])
