;;- Machine description for the Motorola 88000 for GNU C compiler
;;  Copyright (C) 1988, 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000
;;  Free Software Foundation, Inc.
;;  Contributed by Michael Tiemann (tiemann@mcc.com)
;;  Currently maintained by (gcc@dg-rtp.dg.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.


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

;; Attribute describing the processor.  This attribute must match exactly
;; with the processor_type enumeration in m88k.h.

; Target CPU.
(define_attr "cpu" "m88100,m88110,m88000"
  (const (symbol_ref "m88k_cpu")))

; Type of each instruction.  Default is arithmetic.
; I'd like to write the list as this, but genattrtab won't accept it.
;
; "branch,jump,call,			; flow-control instructions
;  load,store,loadd,loada,		; data unit instructions
;  spadd,dpadd,spcmp,dpcmp,spdiv,dpdiv,idiv, ; FPU add instructions
;  spmul,dpmul,imul,			; FPU multiply instructions
;  arith,bit,mov			; integer unit instructions
;  marith,weird"			; multi-word instructions

; Classification of each insn.  Some insns of TYPE_BRANCH are multi-word.
(define_attr "type"
  "branch,jump,call,load,store,loadd,loada,spadd,dpadd,spcmp,dpcmp,spdiv,dpdiv,idiv,spmul,dpmul,imul,arith,bit,mov,marith,weird"
  (const_string "arith"))

(define_attr "fpu" "yes,no"
  (if_then_else
   (eq_attr "type" "spmul,dpmul,imul,spadd,dpadd,spcmp,dpcmp,spdiv,dpdiv,idiv")
   (const_string "yes") (const_string "no")))

; Length in # of instructions of each insn.  The values are not exact, but
; are safe.
(define_attr "length" ""
  (cond [(eq_attr "type" "marith,weird,branch")
	 (const_int 2)]
	(const_int 1)))

; Describe a user's asm statement.
(define_asm_attributes
  [(set_attr "type" "weird")])

; Define the delay slot requirements for branches and calls.
; The m88100 annuls instructions if a conditional branch is taken.
; For insns of TYPE_BRANCH that are multi-word instructions, the
; delay slot applies to the first instruction.

; @@ For the moment, reorg.c requires that the delay slot of a branch not
; be a call or branch.

(define_delay (eq_attr "type" "branch,jump")
  [(and
    (and
     (eq_attr "type" "!branch,jump,call,marith,weird") ; required.
     (eq_attr "type" "!load,loadd")) ; issue as-soon-as-possible.
    (eq_attr "fpu" "no")) ; issue as-soon-as-possible.
   (eq_attr "type" "!call,branch,jump") (nil)]) ; @@ was (const_int 1)

; output_call supports an unconditional branch in the delay slot of
; a call.  (@@ Support for this case is expected in reorg.c soon.)

(define_delay (eq_attr "type" "call")
  [(eq_attr "type" "!branch,call,marith,weird") ; required.
   (nil) (nil)])

; An abstract block diagram of the function units for the m88100.
;
;			    *
;			    |
;			+---v----+
;			| decode |
;			+-vv-v-v-+	 fpu
;	       ,----------'| | `----------------------.
;	       |	   | |			      |	,-----.
;	  load |     store | | arith		      |	|     |
;	       |	   | |			    +-v-v-+   |	dp source
;	       |	   | |			    | fp1 |---'
;     store    |	   | |	    div		    +-v-v-+
;   ,------.   |	   | |	  ,-----. ,-----------'	`-----------.
;   |	   |   |	   | |	  |	| |			    |
;   |	+--v---v--+    ,---' |	  |   +-v-v---+			+---v---+
;   |	| stage	2 |    |     |	  `---|	add 2 |			| mul 2	|
;   |	+---------+    |  +--v--+     +-------+		  imul	+-------+
;   |	| stage	1 |    |  | alu	|     |	add 3 |	       ,--------| mul 3	|
;   |	+---------+    |  +--v--+     +-------+	       |	+-------+
;   |	| stage	0 |    |     |	      |	add 4 |	       |	| mul 4	|
;   |	+--v---v--+    |     |	      +---v---+	       |	+-------+
;   |	   |   |       |     |		  |	       |	| mul 5	|
;   |	   *   |       |     |		  |	       |	+---v---+
;   |	       |       |     |		  |	  +----v----+	    |
;   |	  load |       |     |	   fp add `------>| fp last |<------' fp mul
;   |	       |       |     |			  +---v-v--^+
;   |	       |       |     |			      |	|  |
;   |	       |       |     |			      |	`--' dp	dest
;   |	       |    +--v-----v--+		      |
;   |	       `--->| writeback	|<--------------------'
;   |		    +--v-----v--+
;   |		       |     |
;   `------------------'     *
;
; The decode unit need not be specified.
; Consideration of writeback contention is critical to superb scheduling.
;
; (define_function_unit NAME MULTIPLICITY SIMULTANEITY
;			TEST READY-DELAY ISSUE-DELAY [CONFLICT-LIST])

; Describing the '100 alu is currently not useful.
;(define_function_unit "alu" 1 0 (eq_attr "type"
;					 "!store,marith,weird") 1 0)
;(define_function_unit "alu" 1 0 (eq_attr "type" "marith,weird") 2 0)

(define_function_unit "alu" 1 0
  (and (eq_attr "type" "loada,arith,mov") (eq_attr "cpu" "!m88100")) 2 0)
(define_function_unit "alu" 1 0
  (and (eq_attr "type" "marith,weird") (eq_attr "cpu" "!m88100")) 4 0)

(define_function_unit "bit" 1 0
  (and (eq_attr "type" "bit") (eq_attr "cpu" "!m88100")) 2 2)

(define_function_unit "mem100" 1 0
  (and (eq_attr "type" "store,loada") (eq_attr "cpu" "m88100")) 1 0)
(define_function_unit "mem100" 1 0
  (and (eq_attr "type" "load") (eq_attr "cpu" "m88100")) 3 0)
(define_function_unit "mem100" 1 0
  (and (eq_attr "type" "loadd") (eq_attr "cpu" "m88100")) 3 2)

(define_function_unit "mem110" 1 0
  (and (eq_attr "type" "load,loadd") (eq_attr "cpu" "!m88100")) 3 2)
(define_function_unit "mem110" 1 0
  (and (eq_attr "type" "store") (eq_attr "cpu" "!m88100")) 1 2)

; The times are adjusted to include fp1 and fplast, but then are further
; adjusted based on the actual generated code.  The notation to the right
; is the total latency.  A range denotes a group of instructions and/or
; conditions (the extra clock of fplast time with some sequences).

(define_function_unit "fpmul100" 1 0
  (and (eq_attr "type" "spmul") (eq_attr "cpu" "m88100")) 4 0)		; 6-8
(define_function_unit "fpmul100" 1 0
  (and (eq_attr "type" "dpmul") (eq_attr "cpu" "m88100")) 7 0)		; 9-10
(define_function_unit "fpmul100" 1 0
  (and (eq_attr "type" "imul") (eq_attr "cpu" "m88100")) 3 0)		; 4

(define_function_unit "fpmul110" 1 0
  (and (eq_attr "type" "imul,spmul,dpmul")
       (eq_attr "cpu" "!m88100")) 5 2)					; 3

(define_function_unit "fpadd100" 1 5
  (and (eq_attr "type" "spadd,spcmp") (eq_attr "cpu" "m88100")) 3 0)	; 5-6
(define_function_unit "fpadd100" 1 5
  (and (eq_attr "type" "dpadd,dpcmp") (eq_attr "cpu" "m88100")) 4 0)	; 6-7

(define_function_unit "fpadd110" 1 0
  (and (eq_attr "type" "spadd,dpadd") (eq_attr "cpu" "!m88100")) 5 2)	; 3
(define_function_unit "fpadd110" 1 0
  (and (eq_attr "type" "spcmp,dpcmp") (eq_attr "cpu" "!m88100")) 2 2)	; 1

(define_function_unit "fpadd100" 1 5
  (and (eq_attr "type" "spdiv") (eq_attr "cpu" "m88100")) 30 0)		; 30-31
(define_function_unit "fpadd100" 1 5
  (and (eq_attr "type" "dpdiv") (eq_attr "cpu" "m88100")) 60 0)		; 60-61
(define_function_unit "fpadd100" 1 5
  (and (eq_attr "type" "idiv") (eq_attr "cpu" "m88100")) 38 0)		; 38

(define_function_unit "div" 1 1
  (and (eq_attr "type" "spdiv") (eq_attr "cpu" "!m88100")) 25 2)	; 13
(define_function_unit "div" 1 1
  (and (eq_attr "type" "dpdiv") (eq_attr "cpu" "!m88100")) 45 2)	; 23
(define_function_unit "div" 1 1
  (and (eq_attr "type" "idiv") (eq_attr "cpu" "!m88100")) 35 2)		; 18

;; Superoptimizer sequences

;; geu+: { r = ((unsigned_word) v0 >= (unsigned_word) v1) + v2; }
;;      subu.co r5,r2,r3
;;      addu.cio r6,r4,r0

(define_split
  [(set (match_operand:SI 0 "register_operand" "=r")
	(minus:SI (match_operand:SI 1 "register_operand" "r")
		  (geu:SI (match_operand:SI 2 "register_operand" "r")
			  (match_operand:SI 3 "register_operand" "r"))))]
  ""
  [(set (reg:CC 0) (unspec:CC [(match_dup 2) (match_dup 3)] 1))
   (set (match_dup 0)
	(plus:SI (match_dup 1)
		 (unspec:SI [(const_int 0)
			     (reg:CC 0)] 0)))]
  "")

;; leu+: { r = ((unsigned_word) v0 <= (unsigned_word) v1) + v2; }
;;      subu.co r5,r3,r2
;;      addu.cio r6,r4,r0

(define_split
  [(set (match_operand:SI 0 "register_operand" "=r")
	(minus:SI (match_operand:SI 1 "register_operand" "r")
		  (leu:SI (match_operand:SI 3 "register_operand" "r")
			  (match_operand:SI 2 "register_operand" "r"))))]
  ""
  [(set (reg:CC 0) (unspec:CC [(match_dup 2) (match_dup 3)] 1))
   (set (match_dup 0)
	(plus:SI (match_dup 1)
		 (unspec:SI [(const_int 0)
			     (reg:CC 0)] 0)))]
  "")

;; eq0+: { r = (v0 == 0) + v1; }
;;      subu.co r4,r0,r2
;;      addu.cio r5,r3,r0

(define_split
  [(set (match_operand:SI 0 "register_operand" "=r")
	(minus:SI (match_operand:SI 1 "register_operand" "r")
		  (eq:SI (match_operand:SI 2 "register_operand" "r")
			 (const_int 0))))]
  ""
  [(set (reg:CC 0) (unspec:CC [(const_int 0) (match_dup 2)] 1))
   (set (match_dup 0)
	(plus:SI (match_dup 1)
		 (unspec:SI [(const_int 0)
			     (reg:CC 0)] 0)))]
  "")

;; ltu-:  { r = v2 - ((unsigned_word) v0 < (unsigned_word) v1); }
;;      subu.co r5,r2,r3
;;      subu.cio r6,r4,r0

(define_split
  [(set (match_operand:SI 0 "register_operand" "=r")
	(plus:SI (ltu:SI (match_operand:SI 2 "register_operand" "r")
			 (match_operand:SI 3 "register_operand" "r"))
		 (match_operand:SI 1 "register_operand" "r")))]
  ""
  [(set (reg:CC 0) (unspec:CC [(match_dup 2) (match_dup 3)] 1))
   (set (match_dup 0)
	(minus:SI (match_dup 1)
		  (unspec:SI [(const_int 0)
			      (reg:CC 0)] 1)))]
  "")

;; gtu-: { r = v2 - ((unsigned_word) v0 > (unsigned_word) v1); }
;;      subu.co r5,r3,r2
;;      subu.cio r6,r4,r0

(define_split
  [(set (match_operand:SI 0 "register_operand" "=r")
	(plus:SI (gtu:SI (match_operand:SI 3 "register_operand" "r")
			 (match_operand:SI 2 "register_operand" "r"))
		 (match_operand:SI 1 "register_operand" "r")))]
  ""
  [(set (reg:CC 0) (unspec:CC [(match_dup 2) (match_dup 3)] 1))
   (set (match_dup 0)
	(minus:SI (match_dup 1)
		 (unspec:SI [(const_int 0)
			     (reg:CC 0)] 1)))]
  "")

;; ne0-: { r = v1 - (v0 != 0); }
;;      subu.co r4,r0,r2
;;      subu.cio r5,r3,r0

(define_split
  [(set (match_operand:SI 0 "register_operand" "=r")
	(plus:SI (ne:SI (match_operand:SI 2 "register_operand" "r")
			(const_int 0))
		 (match_operand:SI 1 "register_operand" "r")))]
  ""
  [(set (reg:CC 0) (unspec:CC [(const_int 0) (match_dup 2)] 1))
   (set (match_dup 0)
	(minus:SI (match_dup 1)
		  (unspec:SI [(const_int 0)
			      (reg:CC 0)] 1)))]
  "")

;; ges0-: { r = v1 - ((signed_word) v0 >= 0); }
;;	addu.co	r4,r2,r2
;;	subu.cio r5,r3,r0

(define_split
  [(set (match_operand:SI 0 "register_operand" "=r")
	(minus:SI (match_operand:SI 1 "register_operand" "r")
		  (xor:SI (lshiftrt:SI
			   (match_operand:SI 2 "register_operand" "r")
			   (const_int 31))
			  (const_int 1))))]
  ""
  [(set (reg:CC 0) (unspec:CC [(match_dup 2) (match_dup 2)] 0))
   (set (match_dup 0)
	(minus:SI (match_dup 1)
		  (unspec:SI [(const_int 0)
			      (reg:CC 0)] 1)))]
  "")

;; This rich set of complex patterns are mostly due to Torbjorn Granlund
;; (tege@sics.se).  They've changed since then, so don't complain to him
;; if they don't work right.

;; Regarding shifts, gen_lshlsi3 generates ASHIFT.  The gen functions
;; produce the necessary insns to support TARGET_*_LARGE_SHIFT, so nothing
;; special needs to be done here.

;; Optimize possible cases of the set instruction.

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r")
        (ashift:SI (const_int -1)
       	    (match_operand:SI 1 "register_operand" "r")))]
  ""
  "set %0,%#r0,%1"
  [(set_attr "type" "bit")])

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r")
        (ior:SI (ashift:SI (const_int -1)
       		    (match_operand:SI 1 "register_operand" "r"))
       	 (match_operand:SI 2 "register_operand" "r")))]
  ""
  "set %0,%2,%1"
  [(set_attr "type" "bit")])

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r")
        (ior:SI (match_operand:SI 1 "register_operand" "r")
       	 (ashift:SI (const_int -1)
       		    (match_operand:SI 2 "register_operand" "r"))))]
  ""
  "set %0,%1,%2"
  [(set_attr "type" "bit")])

;; Optimize possible cases of the mak instruction.

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r")
	(and:SI (ashift:SI (match_operand:SI 1 "register_operand" "r")
			   (match_operand:SI 2 "int5_operand" ""))
		(match_operand:SI 3 "immediate_operand" "n")))]
  "mak_mask_p (INTVAL (operands[3]) >> INTVAL (operands[2]))"
  "*
{
  operands[4] = GEN_INT (exact_log2 (1 + (INTVAL (operands[3])
					  >> INTVAL(operands[2]))));
  return \"mak %0,%1,%4<%2>\";
}"
  [(set_attr "type" "bit")])

;; Optimize possible cases of output_and.

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r")
	(ashift:SI (zero_extract:SI (match_operand:SI 1 "register_operand" "r")
				    (match_operand:SI 2 "int5_operand" "")
				    (match_operand:SI 3 "int5_operand" ""))
		   (match_operand:SI 4 "int5_operand" "")))]
  "INTVAL (operands[2]) + INTVAL (operands[3]) + INTVAL (operands[4]) == 32"
  "*
{
  operands[2]
    = GEN_INT (((1 << INTVAL (operands[2])) - 1) << INTVAL (operands[4]));
  return output_and (operands);
}"
  [(set_attr "type" "marith")]) ; arith,bit,marith.  length is 1 or 2.

;; Improve logical operations on compare words
;;
;; We define all logical operations on CCmode values to preserve the pairwise
;; relationship of the compare bits.  This allows a future branch prediction
;; pass the degree of freedom needed to change and/bb0-le into or/bb1-gt.
;; THIS IS CURRENTLY FALSE! 
;;
;; Opportunities arise when conditional expressions using && and || are made
;; unconditional.  When these are used to branch, the sequence is
;; cmp/cmp/extu/extu/{and,or}/bcnd-{eq0,ne0}.  When these are used to create
;; a value, the sequence is cmp/cmp/extu/extu/{and,or} for 1 or 0 or
;; cmp/cmp/ext/ext/{and,or} for -1 or 0.
;;
;; When the extracted conditions are the same, the define_split patterns
;; below change extu/extu/{and,or} into {and,or}/extu.  If the reversed
;; conditions match, one compare word can be complimented, resulting in
;; {and.c,or.c}/extu.  These changes are done for ext/ext/{and,or} as well.
;; If the conditions don't line up, one can be rotated.  To keep the pairwise
;; relationship, it may be necessary to both rotate and compliment.  Rotating
;; makes branching cheaper, but doesn't help (or hurt) creating a value, so
;; we don't do this for ext/ext/{and,or}.
;;
;; These changes result in the sequence extu/bcnd-{eq0,ne0} which is combined
;; into an alternate form of bb0 and bb1.

(define_split
  [(set (match_operand:SI 0 "register_operand" "=r")
	(ior:SI (neg:SI 
		 (match_operator 1 "even_relop"
				 [(match_operand 2 "partial_ccmode_register_operand" "%r")
				  (const_int 0)]))
		(neg:SI
		 (match_operator 3 "relop"
				 [(match_operand 4 "partial_ccmode_register_operand" "r")
				  (const_int 0)]))))
   (clobber (match_operand:SI 5 "register_operand" "=r"))]
  ""
  [(set (match_dup 5)
	(ior:CCEVEN (match_dup 4)
		(match_dup 2)))
   (set (match_dup 0)
	(neg:SI (match_op_dup 1 [(match_dup 5) (const_int 0)])))]
  "operands[5] = gen_rtx_SUBREG (CCEVENmode, operands[5], 0);
   if (GET_CODE (operands[1]) == GET_CODE (operands[3]))
     ; /* The conditions match.  */
   else if (GET_CODE (operands[1])
	    == reverse_condition (GET_CODE (operands[3])))
     /* Reverse the condition by complimenting the compare word.  */
     operands[4] = gen_rtx_NOT (CCmode, operands[4]);
   else
     {
       /* Make the condition pairs line up by rotating the compare word.  */
       int cv1 = condition_value (operands[1]);
       int cv2 = condition_value (operands[3]);

       operands[4] = gen_rtx_ROTATE (CCmode, operands[4],
				     GEN_INT (((cv2 & ~1) - (cv1 & ~1))
					      & 0x1f));
       /* Reverse the condition if needed.  */
       if ((cv1 & 1) != (cv2 & 1))
	 operands[4] = gen_rtx_NOT (CCmode, operands[4]);
     }")

(define_split
  [(set (match_operand:SI 0 "register_operand" "=r")
	(ior:SI (neg:SI 
		 (match_operator 1 "odd_relop"
				 [(match_operand 2 "partial_ccmode_register_operand" "%r")
				  (const_int 0)]))
		(neg:SI
		 (match_operator 3 "odd_relop"
				 [(match_operand 4 "partial_ccmode_register_operand" "r")
				  (const_int 0)]))))
   (clobber (match_operand:SI 5 "register_operand" "=r"))]
  ""
  [(set (match_dup 5)
	(and:CCEVEN (match_dup 4)
		(match_dup 2)))
   (set (match_dup 0)
	(neg:SI (match_op_dup 1 [(match_dup 5) (const_int 0)])))]
  "operands[5] = gen_rtx_SUBREG (CCEVENmode, operands[5], 0);
   if (GET_CODE (operands[1]) == GET_CODE (operands[3]))
     ; /* The conditions match.  */
   else
     {
       /* Make the condition pairs line up by rotating the compare word.  */
       int cv1 = condition_value (operands[1]);
       int cv2 = condition_value (operands[3]);

       operands[4] = gen_rtx_ROTATE (CCmode, operands[4],
				     GEN_INT ((cv2 - cv1) & 0x1f));
     }")

(define_split
  [(set (match_operand:SI 0 "register_operand" "=r")
	(ior:SI (neg:SI 
		 (match_operator 1 "odd_relop"
				 [(match_operand 2 "partial_ccmode_register_operand" "%r")
				  (const_int 0)]))
		(neg:SI
		 (match_operator 3 "even_relop"
				 [(match_operand 4 "partial_ccmode_register_operand" "r")
				  (const_int 0)]))))
   (clobber (match_operand:SI 5 "register_operand" "=r"))]
  ""
  [(set (match_dup 5)
	(ior:CCEVEN (not:CC (match_dup 2))
		(match_dup 4)))
   (set (match_dup 0)
	(neg:SI (match_op_dup 3 [(match_dup 5) (const_int 0)])))]
  "operands[5] = gen_rtx_SUBREG (CCEVENmode, operands[5], 0);
  if (GET_CODE (operands[1])
	    == reverse_condition (GET_CODE (operands[3])))
     ; 
   else
     {
       /* Make the condition pairs line up by rotating the compare word.  */
       int cv1 = condition_value (operands[1]);
       int cv2 = condition_value (operands[3]);

       operands[2] = gen_rtx_ROTATE (CCmode, operands[2],
				     GEN_INT (((cv1 & ~1) - (cv2 & ~1))
					      & 0x1f));
     }")

(define_split
  [(set (match_operand:SI 0 "register_operand" "=r")
	(ior:SI (match_operator 1 "even_relop"
				[(match_operand 2 "partial_ccmode_register_operand" "%r")
				 (const_int 0)])
		(match_operator 3 "relop"
				[(match_operand 4 "partial_ccmode_register_operand" "r")
				 (const_int 0)])))
   (clobber (match_operand:SI 5 "register_operand" "=r"))]
  "GET_CODE (operands[1]) == GET_CODE (operands[3])
   || GET_CODE (operands[1]) == reverse_condition (GET_CODE (operands[3]))"
  [(set (match_dup 5)
	(ior:CCEVEN (match_dup 4)
		(match_dup 2)))
   (set (match_dup 0)
	(match_op_dup 1 [(match_dup 5) (const_int 0)]))]
  "operands[5] = gen_rtx_SUBREG (CCEVENmode, operands[5], 0);
   /* Reverse the condition by  complimenting the compare word.  */
   if (GET_CODE (operands[1]) != GET_CODE (operands[3]))
      operands[4] = gen_rtx_NOT (CCmode, operands[4]);")

(define_split
  [(set (match_operand:SI 0 "register_operand" "=r")
	(ior:SI (match_operator 1 "odd_relop"
				[(match_operand 2 "partial_ccmode_register_operand" "%r")
				 (const_int 0)])
		(match_operator 3 "odd_relop"
				[(match_operand 4 "partial_ccmode_register_operand" "r")
				 (const_int 0)])))
   (clobber (match_operand:SI 5 "register_operand" "=r"))]
  "GET_CODE (operands[1]) == GET_CODE (operands[3])"
  [(set (match_dup 5)
	(and:CCEVEN (match_dup 4)
		(match_dup 2)))
   (set (match_dup 0)
	(match_op_dup 1 [(match_dup 5) (const_int 0)]))]
  "operands[5] = gen_rtx_SUBREG (CCEVENmode, operands[5], 0);")

(define_split
  [(set (match_operand:SI 0 "register_operand" "=r")
	(ior:SI (match_operator 1 "odd_relop"
				[(match_operand 2 "partial_ccmode_register_operand" "%r")
				 (const_int 0)])
		(match_operator 3 "even_relop"
				[(match_operand 4 "partial_ccmode_register_operand" "r")
				 (const_int 0)])))
   (clobber (match_operand:SI 5 "register_operand" "=r"))]
  "GET_CODE (operands[1]) == reverse_condition (GET_CODE (operands[3]))"
  [(set (match_dup 5)
	(ior:CCEVEN (not:CC (match_dup 4))
		(match_dup 2)))
   (set (match_dup 0)
	(match_op_dup 1 [(match_dup 5) (const_int 0)]))]
  "operands[5] = gen_rtx_SUBREG (CCEVENmode, operands[5], 0);")

(define_split
  [(set (match_operand:SI 0 "register_operand" "=r")
	(and:SI (neg:SI 
		 (match_operator 1 "even_relop"
				 [(match_operand 2 "partial_ccmode_register_operand" "%r")
				  (const_int 0)]))
		(neg:SI
		 (match_operator 3 "relop"
				 [(match_operand 4 "partial_ccmode_register_operand" "r")
				  (const_int 0)]))))
   (clobber (match_operand:SI 5 "register_operand" "=r"))]
  ""
  [(set (match_dup 5)
	(and:CCEVEN (match_dup 4)
		(match_dup 2)))
   (set (match_dup 0)
	(neg:SI (match_op_dup 1 [(match_dup 5) (const_int 0)])))]
  "operands[5] = gen_rtx_SUBREG (CCEVENmode, operands[5], 0);
   if (GET_CODE (operands[1]) == GET_CODE (operands[3]))
     ; /* The conditions match.  */
   else if (GET_CODE (operands[1])
	    == reverse_condition (GET_CODE (operands[3])))
     /* Reverse the condition by complimenting the compare word.  */
     operands[4] = gen_rtx_NOT (CCmode, operands[4]);
   else
     {
       /* Make the condition pairs line up by rotating the compare word.  */
       int cv1 = condition_value (operands[1]);
       int cv2 = condition_value (operands[3]);
       operands[4] = gen_rtx_ROTATE (CCmode, operands[4],
				     GEN_INT (((cv2 & ~1) - (cv1 & ~1))
					      & 0x1f));
       /* Reverse the condition if needed.  */
       if ((cv1 & 1) != (cv2 & 1))
	 operands[4] = gen_rtx_NOT (CCmode, operands[4]);
     }")

(define_split
  [(set (match_operand:SI 0 "register_operand" "=r")
	(and:SI (neg:SI 
		 (match_operator 1 "odd_relop"
				 [(match_operand 2 "partial_ccmode_register_operand" "%r")
				  (const_int 0)]))
		(neg:SI
		 (match_operator 3 "odd_relop"
				 [(match_operand 4 "partial_ccmode_register_operand" "r")
				  (const_int 0)]))))
   (clobber (match_operand:SI 5 "register_operand" "=r"))]
  ""
  [(set (match_dup 5)
	(ior:CCEVEN (match_dup 4)
		(match_dup 2)))
   (set (match_dup 0)
	(neg:SI (match_op_dup 1 [(match_dup 5) (const_int 0)])))]
  "operands[5] = gen_rtx_SUBREG (CCEVENmode, operands[5], 0);
   if (GET_CODE (operands[1]) == GET_CODE (operands[3]))
     ; /* The conditions match.  */
   else
     {
       /* Make the condition pairs line up by rotating the compare word.  */
       int cv1 = condition_value (operands[1]);
       int cv2 = condition_value (operands[3]);
       operands[4] = gen_rtx_ROTATE (CCmode, operands[4],
				     GEN_INT ((cv2 - cv1) & 0x1f));
     }")

(define_split
  [(set (match_operand:SI 0 "register_operand" "=r")
	(and:SI (neg:SI 
		 (match_operator 1 "odd_relop"
				 [(match_operand 2 "partial_ccmode_register_operand" "%r")
				  (const_int 0)]))
		(neg:SI
		 (match_operator 3 "even_relop"
				 [(match_operand 4 "partial_ccmode_register_operand" "r")
				  (const_int 0)]))))
   (clobber (match_operand:SI 5 "register_operand" "=r"))]
  ""
  [(set (match_dup 5)
	(and:CCEVEN (not:CC (match_dup 2))
		(match_dup 4)))
   (set (match_dup 0)
	(neg:SI (match_op_dup 3 [(match_dup 5) (const_int 0)])))]
  "operands[5] = gen_rtx_SUBREG (CCEVENmode, operands[5], 0);
   if (GET_CODE (operands[1])
	    == reverse_condition (GET_CODE (operands[3])))
	;
   else
     {
       /* Make the condition pairs line up by rotating the compare word.  */
       int cv1 = condition_value (operands[1]);
       int cv2 = condition_value (operands[3]);
       operands[2] = gen_rtx_ROTATE (CCmode, operands[2],
				     GEN_INT (((cv1 & ~1) - (cv2 & ~1))
					      & 0x1f));
     }")

(define_split
  [(set (match_operand:SI 0 "register_operand" "=r")
	(and:SI (match_operator 1 "even_relop"
				[(match_operand 2 "partial_ccmode_register_operand" "%r")
				 (const_int 0)])
		(match_operator 3 "relop"
				[(match_operand 4 "partial_ccmode_register_operand" "r")
				 (const_int 0)])))
   (clobber (match_operand:SI 5 "register_operand" "=r"))]
  "GET_CODE (operands[1]) == GET_CODE (operands[3])
   || GET_CODE (operands[1]) == reverse_condition (GET_CODE (operands[3]))"
  [(set (match_dup 5)
	(and:CCEVEN (match_dup 4)
		(match_dup 2)))
   (set (match_dup 0)
	(match_op_dup 1 [(match_dup 5) (const_int 0)]))]
  "operands[5] = gen_rtx_SUBREG (CCEVENmode, operands[5], 0);
   /* Reverse the condition by  complimenting the compare word.  */
   if (GET_CODE (operands[1]) != GET_CODE (operands[3]))
      operands[4] = gen_rtx_NOT (CCmode, operands[4]);")

(define_split
  [(set (match_operand:SI 0 "register_operand" "=r")
	(and:SI (match_operator 1 "odd_relop"
				[(match_operand 2 "partial_ccmode_register_operand" "%r")
				 (const_int 0)])
		(match_operator 3 "odd_relop"
				[(match_operand 4 "partial_ccmode_register_operand" "r")
				 (const_int 0)])))
   (clobber (match_operand:SI 5 "register_operand" "=r"))]
  "GET_CODE (operands[1]) == GET_CODE (operands[3])"
  [(set (match_dup 5)
	(ior:CCEVEN (match_dup 4)
		(match_dup 2)))
   (set (match_dup 0)
	(match_op_dup 1 [(match_dup 5) (const_int 0)]))]
  "operands[5] = gen_rtx_SUBREG (CCEVENmode, operands[5], 0);")

(define_split
  [(set (match_operand:SI 0 "register_operand" "=r")
	(and:SI (match_operator 1 "odd_relop"
				[(match_operand 2 "partial_ccmode_register_operand" "%r")
				 (const_int 0)])
		(match_operator 3 "even_relop"
				[(match_operand 4 "partial_ccmode_register_operand" "r")
				 (const_int 0)])))
   (clobber (match_operand:SI 5 "register_operand" "=r"))]
  "GET_CODE (operands[1]) == reverse_condition (GET_CODE (operands[3]))"
  [(set (match_dup 5)
	(and:CCEVEN (not:CC (match_dup 2))
		(match_dup 4)))
   (set (match_dup 0)
	(match_op_dup 3 [(match_dup 5) (const_int 0)]))]
  "operands[5] = gen_rtx_SUBREG (CCEVENmode, operands[5], 0);")


;; Logical operations on compare words.

(define_insn ""
  [(set (match_operand:CCEVEN 0 "register_operand" "=r")
	(and:CCEVEN (not:CC (match_operand 1 "partial_ccmode_register_operand" "r"))
		(match_operand 2 "partial_ccmode_register_operand" "r")))]
  ""
  "and.c %0,%2,%1")

(define_insn ""
  [(set (match_operand:CCEVEN 0 "register_operand" "=r")
	(and:CCEVEN (match_operand 1 "partial_ccmode_register_operand" "%r")
		(match_operand 2 "partial_ccmode_register_operand" "r")))]
  ""
  "and %0,%1,%2")

(define_insn ""
  [(set (match_operand:CCEVEN 0 "register_operand" "=r")
	(ior:CCEVEN (not:CC (match_operand 1 "partial_ccmode_register_operand" "r"))
		(match_operand 2 "partial_ccmode_register_operand" "r")))]
  ""
  "or.c %0,%2,%1")

(define_insn ""
  [(set (match_operand:CCEVEN 0 "register_operand" "=r")
	(ior:CCEVEN (match_operand 1 "partial_ccmode_register_operand" "%r")
		(match_operand 2 "partial_ccmode_register_operand" "r")))]
  ""
  "or %0,%1,%2")

(define_insn ""
  [(set (match_operand:CC 0 "register_operand" "=r")
	(rotate:CC (match_operand:CC 1 "register_operand" "r")
		   (match_operand:CC 2 "int5_operand" "")))]
  ""
  "rot %0,%1,%2"
  [(set_attr "type" "bit")])

(define_insn ""
  [(set (match_operand:CCEVEN 0 "register_operand" "=r")
	(rotate:CCEVEN (match_operand 1 "partial_ccmode_register_operand" "r")
		   (match_operand:CC 2 "int5_operand" "")))]
  ""
  "rot %0,%1,%2"
  [(set_attr "type" "bit")])

;; rotate/and[.c] and rotate/ior[.c]

(define_split
  [(set (match_operand:CCEVEN 0 "register_operand" "=r")
	(ior:CCEVEN (rotate:CC (match_operand 1 "partial_ccmode_register_operand" "r")
			   (match_operand:CC 2 "int5_operand" ""))
		(match_operand 3 "partial_ccmode_register_operand" "r")))
   (clobber (match_operand:CCEVEN 4 "register_operand" "=r"))]
  ""
  [(set (match_dup 4)
	(rotate:CC (match_dup 1) (match_dup 2)))
   (set (match_dup 0)
	(ior:CCEVEN (match_dup 4) (match_dup 3)))]
  "")

(define_insn ""
  [(set (match_operand:CCEVEN 0 "register_operand" "=r")
	(ior:CCEVEN (rotate:CC (match_operand 1 "partial_ccmode_register_operand" "r")
			   (match_operand:CC 2 "int5_operand" ""))
		(match_operand 3 "partial_ccmode_register_operand" "r")))
   (clobber (match_scratch:CCEVEN 4 "=r"))]
  ""
  "#")

(define_split
  [(set (match_operand:CCEVEN 0 "register_operand" "=r")
	(ior:CCEVEN (not:CC (rotate:CC (match_operand 1 "partial_ccmode_register_operand" "r")
				   (match_operand:CC 2 "int5_operand" "")))
		(match_operand 3 "partial_ccmode_register_operand" "r")))
   (clobber (match_operand:CCEVEN 4 "register_operand" "=r"))]
  ""
  [(set (match_dup 4)
	(rotate:CC (match_dup 1) (match_dup 2)))
   (set (match_dup 0)
	(ior:CCEVEN (not:CC (match_dup 4)) (match_dup 3)))]
  "")

(define_insn ""
  [(set (match_operand:CCEVEN 0 "register_operand" "=r")
	(ior:CCEVEN (not:CC (rotate:CC (match_operand 1 "partial_ccmode_register_operand" "r")
				   (match_operand:CC 2 "int5_operand" "")))
		(match_operand 3 "partial_ccmode_register_operand" "r")))
   (clobber (match_scratch:CCEVEN 4 "=r"))]
  ""
  "#")

(define_split
  [(set (match_operand:CCEVEN 0 "register_operand" "=r")
	(and:CCEVEN (rotate:CC (match_operand 1 "partial_ccmode_register_operand" "r")
			   (match_operand:CC 2 "int5_operand" ""))
		(match_operand 3 "partial_ccmode_register_operand" "r")))
   (clobber (match_operand:CCEVEN 4 "register_operand" "=r"))]
  ""
  [(set (match_dup 4)
	(rotate:CC (match_dup 1) (match_dup 2)))
   (set (match_dup 0)
	(and:CCEVEN (match_dup 4) (match_dup 3)))]
  "")

(define_insn ""
  [(set (match_operand:CCEVEN 0 "register_operand" "=r")
	(and:CCEVEN (rotate:CC (match_operand 1 "partial_ccmode_register_operand" "r")
			   (match_operand:CC 2 "int5_operand" ""))
		(match_operand 3 "partial_ccmode_register_operand" "r")))
   (clobber (match_scratch:CCEVEN 4 "=r"))]
  ""
  "#")

(define_split
  [(set (match_operand:CCEVEN 0 "register_operand" "=r")
	(and:CCEVEN (not:CC (rotate:CC (match_operand 1 "partial_ccmode_register_operand" "r")
				   (match_operand:CC 2 "int5_operand" "")))
		(match_operand 3 "partial_ccmode_register_operand" "r")))
   (clobber (match_operand:CCEVEN 4 "register_operand" "=r"))]
  ""
  [(set (match_dup 4)
	(rotate:CC (match_dup 1) (match_dup 2)))
   (set (match_dup 0)
	(and:CCEVEN (not:CC (match_dup 4)) (match_dup 3)))]
  "")

(define_insn ""
  [(set (match_operand:CCEVEN 0 "register_operand" "=r")
	(and:CCEVEN (not:CC (rotate:CC (match_operand 1 "partial_ccmode_register_operand" "r")
				   (match_operand:CC 2 "int5_operand" "")))
		(match_operand 3 "partial_ccmode_register_operand" "r")))
   (clobber (match_scratch:CCEVEN 4 "=r"))]
  ""
  "#")


;; Recognize bcnd instructions for integer values.  This is distinguished
;; from a conditional branch instruction (below) with SImode instead of
;; CCmode.

(define_insn ""
  [(set (pc)
	(if_then_else
	 (match_operator 0 "relop_no_unsigned"
			 [(match_operand:SI 1 "register_operand" "r")
			  (const_int 0)])
	 (match_operand 2 "pc_or_label_ref" "")
	 (match_operand 3 "pc_or_label_ref" "")))]
  ""
  "bcnd%. %R3%B0,%1,%P2%P3"
  [(set_attr "type" "branch")])

;; Recognize tests for sign and zero.

(define_insn ""
  [(set (pc)
	(if_then_else
	 (match_operator 0 "equality_op"
			 [(match_operand:SI 1 "register_operand" "r")
			  (const_int -2147483648)])
	 (match_operand 2 "pc_or_label_ref" "")
	 (match_operand 3 "pc_or_label_ref" "")))]
  ""
  "bcnd%. %R3%E0,%1,%P2%P3"
  [(set_attr "type" "branch")])

(define_insn ""
  [(set (pc)
	(if_then_else
	 (match_operator 0 "equality_op"
			 [(zero_extract:SI
			   (match_operand:SI 1 "register_operand" "r")
			   (const_int 31)
			   (const_int 1))
			  (const_int 0)])
	 (match_operand 2 "pc_or_label_ref" "")
	 (match_operand 3 "pc_or_label_ref" "")))]
  ""
  "bcnd%. %R3%D0,%1,%P2%P3"
  [(set_attr "type" "branch")])

;; Recognize bcnd instructions for double integer values

(define_insn ""
  [(set (pc)
	(if_then_else
	 (match_operator 0 "relop_no_unsigned"
			 [(sign_extend:DI
			   (match_operand:SI 1 "register_operand" "r"))
			  (const_int 0)])
	 (match_operand 2 "pc_or_label_ref" "")
	 (match_operand 3 "pc_or_label_ref" "")))]
  ""
  "bcnd%. %R3%B0,%1,%P2%P3"
  [(set_attr "type" "branch")])

(define_insn ""
  [(set (pc)
	(if_then_else
	 (match_operator 0 "equality_op"
			 [(zero_extend:DI
			   (match_operand:SI 1 "register_operand" "r"))
			  (const_int 0)])
	 (match_operand 2 "pc_or_label_ref" "")
	 (match_operand 3 "pc_or_label_ref" "")))]
  ""
  "bcnd%. %R3%B0,%1,%P2%P3"
  [(set_attr "type" "branch")])

; @@ I doubt this is interesting until cmpdi is provided.  Anyway, it needs
; to be reworked.
;
;(define_insn ""
;  [(set (pc)
;	(if_then_else
;	 (match_operator 0 "relop_no_unsigned"
;			 [(match_operand:DI 1 "register_operand" "r")
;			  (const_int 0)])
;	 (match_operand 2 "pc_or_label_ref" "")
;	 (match_operand 3 "pc_or_label_ref" "")))]
;  ""
;  "*
;{
;  switch (GET_CODE (operands[0]))
;    {
;    case EQ:
;    case NE:
;      /* I'm not sure if it's safe to use .n here.  */
;      return \"or %!,%1,%d1\;bcnd %R3%B0,%!,%P2%P3\";
;    case GE:
;    case LT:
;      return \"bcnd%. %R3%B0,%1,%P2%P3\";
;    case GT:
;      {
;	rtx op2 = operands[2];
;	operands[2] = operands[3];
;	operands[3] = op2;
;      }
;    case LE:
;      if (GET_CODE (operands[3]) == LABEL_REF)
;	{
;	  int label_num;
;	  operands[2] = gen_label_rtx ();
;	  label_num = XINT (operands[2], 3);
;	  output_asm_insn
;	    (\"bcnd%. %#lt0,%1,%2\;or %!,%1,%d1\;bcnd %#ne0,%!,%3\", operands);
;	  output_label (label_num);
;	  return \"\";
;	}
;      else
;	return \"bcnd%. %#lt0,%1,%2\;or %!,%1,%d1\;bcnd %#eq0,%!,%2\";
;    }
;}")

;; Recognize bcnd instructions for single precision float values
;; Exclude relational operations as they must signal NaNs.

;; @@ These bcnd insns for float and double values don't seem to be recognized.

(define_insn ""
  [(set (pc)
	(if_then_else
	 (match_operator 0 "equality_op"
			 [(float_extend:DF
			   (match_operand:SF 1 "register_operand" "r"))
			  (const_int 0)])
	 (match_operand 2 "pc_or_label_ref" "")
	 (match_operand 3 "pc_or_label_ref" "")))]
  ""
  "bcnd%. %R3%D0,%1,%P2%P3"
  [(set_attr "type" "branch")])

(define_insn ""
  [(set (pc)
	(if_then_else
	 (match_operator 0 "equality_op"
			 [(match_operand:SF 1 "register_operand" "r")
			  (const_int 0)])
	 (match_operand 2 "pc_or_label_ref" "")
	 (match_operand 3 "pc_or_label_ref" "")))]
  ""
  "bcnd%. %R3%D0,%1,%P2%P3"
  [(set_attr "type" "branch")])

;; Recognize bcnd instructions for double precision float values
;; Exclude relational operations as they must signal NaNs.

(define_insn ""
  [(set (pc)
	(if_then_else
	 (match_operator 0 "equality_op"
			 [(match_operand:DF 1 "register_operand" "r")
			  (const_int 0)])
	 (match_operand 2 "pc_or_label_ref" "")
	 (match_operand 3 "pc_or_label_ref" "")))]
  ""
  "*
{
  int label_num;

  if (GET_CODE (operands[0]) == NE)
    {
      rtx op2 = operands[2];
      operands[2] = operands[3];
      operands[3] = op2;
    }
  if (GET_CODE (operands[3]) == LABEL_REF)
    return \"bcnd 0x5,%1,%3\;bcnd %#ne0,%d1,%3\";

  operands[3] = gen_label_rtx ();
  label_num = XINT (operands[3], 3);
  output_asm_insn (\"bcnd 0x5,%1,%3\;bcnd %#eq0,%d1,%2\", operands);
  output_label (label_num);
  return \"\";
}"
  [(set_attr "type" "weird")
   (set_attr "length" "3")])

;; Recognize bb0 and bb1 instructions.  These use two unusual template
;; patterns, %Lx and %Px.  %Lx outputs a 1 if operand `x' is a LABEL_REF
;; otherwise it outputs a 0.  It then may print ".n" if the delay slot
;; is used.  %Px does noting if `x' is PC and outputs the operand if `x'
;; is a LABEL_REF.

(define_insn ""
  [(set (pc)
	(if_then_else
	 (ne (sign_extract:SI (match_operand:SI 0 "register_operand" "r")
			      (const_int 1)
			      (match_operand:SI 1 "int5_operand" ""))
	     (const_int 0))
	 (match_operand 2 "pc_or_label_ref" "")
	 (match_operand 3 "pc_or_label_ref" "")))]
  ""
  "bb%L2 (31-%1),%0,%P2%P3"
  [(set_attr "type" "branch")])

(define_insn ""
  [(set (pc)
	(if_then_else
	 (eq (sign_extract:SI (match_operand:SI 0 "register_operand" "r")
			      (const_int 1)
			      (match_operand:SI 1 "int5_operand" ""))
	     (const_int 0))
	 (match_operand 2 "pc_or_label_ref" "")
	 (match_operand 3 "pc_or_label_ref" "")))]
  ""
  "bb%L3 (31-%1),%0,%P2%P3"
  [(set_attr "type" "branch")])

(define_insn ""
  [(set (pc)
	(if_then_else
	 (ne (zero_extract:SI (match_operand:SI 0 "register_operand" "r")
			      (const_int 1)
			      (match_operand:SI 1 "int5_operand" ""))
	     (const_int 0))
	 (match_operand 2 "pc_or_label_ref" "")
	 (match_operand 3 "pc_or_label_ref" "")))]
  ""
  "bb%L2 (31-%1),%0,%P2%P3"
  [(set_attr "type" "branch")])

(define_insn ""
  [(set (pc)
	(if_then_else
	 (eq (zero_extract:SI (match_operand:SI 0 "register_operand" "r")
			      (const_int 1)
			      (match_operand:SI 1 "int5_operand" ""))
	     (const_int 0))
	 (match_operand 2 "pc_or_label_ref" "")
	 (match_operand 3 "pc_or_label_ref" "")))]
  ""
  "bb%L3 (31-%1),%0,%P2%P3"
  [(set_attr "type" "branch")])

(define_insn ""
  [(set (pc)
	(if_then_else
	 (eq (and:SI (match_operand:SI 0 "reg_or_bbx_mask_operand" "%r")
		     (match_operand:SI 1 "reg_or_bbx_mask_operand" "n"))
	      (const_int 0))
	 (match_operand 2 "pc_or_label_ref" "")
	 (match_operand 3 "pc_or_label_ref" "")))]
  "(GET_CODE (operands[0]) == CONST_INT)
   != (GET_CODE (operands[1]) == CONST_INT)"
  "bb%L3 %p1,%0,%P2%P3"
  [(set_attr "type" "branch")])

(define_insn ""
  [(set (pc)
	(if_then_else
	 (ne (and:SI (match_operand:SI 0 "reg_or_bbx_mask_operand" "%r")
		     (match_operand:SI 1 "reg_or_bbx_mask_operand" "n"))
	     (const_int 0))
	 (match_operand 2 "pc_or_label_ref" "")
	 (match_operand 3 "pc_or_label_ref" "")))]
  "(GET_CODE (operands[0]) == CONST_INT)
   != (GET_CODE (operands[1]) == CONST_INT)"
  "bb%L2 %p1,%0,%P2%P3"
  [(set_attr "type" "branch")])

;; The comparison operations store the comparison into a register and
;; record that register.  The following Bxx or Sxx insn uses that
;; register as an input.  To facilitate use of bcnd instead of cmp/bb1,
;; cmpsi records its operands and produces no code when any operand
;; is constant.  In this case, the Bxx insns use gen_bcnd and the
;; Sxx insns use gen_test to ensure a cmp has been emitted.
;;
;; This could also be done for SFmode and DFmode having only beq and bne
;; use gen_bcnd.  The others must signal NaNs.  It seems though that zero
;; has already been copied into a register.
;;
;; cmpsi/beq and cmpsi/bne can always be done with bcnd if any operand
;; is a constant.  (This idea is due to Torbjorn Granlund.)  Others can
;; use bcnd only if an operand is zero.
;;
;; It is necessary to distinguish a register holding condition codes.
;; This is done by context.

(define_expand "test"
  [(set (match_dup 2)
	(compare:CC (match_operand 0 "" "")
		    (match_operand 1 "" "")))]
  ""
  "
{
  if (m88k_compare_reg)
    abort ();

  if (GET_CODE (operands[0]) == CONST_INT
      && ! SMALL_INT (operands[0]))
    operands[0] = force_reg (SImode, operands[0]);

  if (GET_CODE (operands[1]) == CONST_INT
      && ! SMALL_INT (operands[1]))
    operands[1] = force_reg (SImode, operands[1]);

  operands[2] = m88k_compare_reg = gen_reg_rtx (CCmode);
}")

; @@ The docs say don't do this.  It's probably a nop since the insn looks
; identical to cmpsi against zero.  Is there an advantage to providing
; this, perhaps with a different form?

;(define_expand "tstsi"
;  [(set (match_dup 1)
;	(compare:CC (match_operand:SI 0 "register_operand" "")
;		    (const_int 0)))]
; ""
; "
;{
;  m88k_compare_reg = 0;
;  m88k_compare_op0 = operands[0];
;  m88k_compare_op1 = const0_rtx;
;  DONE;
;}")

(define_expand "cmpsi"
  [(set (match_dup 2)
	(compare:CC (match_operand:SI 0 "register_operand" "")
		    (match_operand:SI 1 "arith32_operand" "")))]
  ""
  "
{
  if (GET_CODE (operands[0]) == CONST_INT
      || GET_CODE (operands[1]) == CONST_INT)
    {
      m88k_compare_reg = 0;
      m88k_compare_op0 = operands[0];
      m88k_compare_op1 = operands[1];
      DONE;
    }
  operands[2] = m88k_compare_reg = gen_reg_rtx (CCmode);
}")

(define_expand "cmpsf"
  [(set (match_dup 2)
	(compare:CC (match_operand:SF 0 "register_operand" "")
		    (match_operand:SF 1 "register_operand" "")))]
  ""
  "operands[2] = m88k_compare_reg = gen_reg_rtx (CCmode);")

(define_expand "cmpdf"
  [(set (match_dup 2)
	(compare:CC (match_operand:DF 0 "general_operand" "")
		    (match_operand:DF 1 "general_operand" "")))]
  ""
  "
{
  operands[0] = legitimize_operand (operands[0], DFmode);
  operands[1] = legitimize_operand (operands[1], DFmode);
  operands[2] = m88k_compare_reg = gen_reg_rtx (CCmode);
}")

; @@ Get back to this later on.
;
;(define_insn "cmpdi"
;  [(set (cc0)
;	(compare:CC (match_operand:DI 0 "register_operand" "r")
;		    (match_operand:DI 1 "register_operand" "r")))]
;  ""
;  "*
;{
;  if ((cc_status.mdep & MDEP_LS_CHANGE) != 0)
;    abort ();	/* output_move_double MDEP_LS_CHANGE bits were set. */
;
;  cc_status.mdep &= ~ MDEP_LS_MASK;
;
;  operands[2] = gen_label_rtx ();
;  /* Remember, %! is the condition code register and %@ is the
;     literal synthesis register.  */
;
;  output_asm_insn (\"cmp %!,%0,%1\;bb0 %#eq,%!,%l2\;cmp %!,%d0,%d1\",
;		   operands);
;
;  output_asm_insn (\"extu %@,%!,4<8>\;clr %!,%!,4<4>\", operands);
;  output_asm_insn (\"mak %@,%@,4<4>\;or %!,%!,%@\", operands);
;  output_label (XINT (operands[2], 3));
;  return \"\";
;}"

;; The actual compare instructions.

(define_insn ""
  [(set (match_operand:CC 0 "register_operand" "=r")
	(compare:CC (match_operand:SI 1 "register_operand" "rO")
		    (match_operand:SI 2 "arith_operand" "rI")))]
  ""
  "cmp %0,%r1,%2")

(define_insn ""
  [(set (match_operand:CC 0 "register_operand" "=r,r,r,r")
	(compare:CC (match_operand:SF 1 "register_operand" "r,r,x,x")
		    (match_operand:SF 2 "real_or_0_operand" "r,G,x,G")))]
  ""
  "@
   fcmp.sss %0,%1,%2
   fcmp.sss %0,%1,%#r0
   fcmp.sss %0,%1,%2
   fcmp.sss %0,%1,%#x0"
  [(set_attr "type" "spcmp")])

(define_insn ""
  [(set (match_operand:CC 0 "register_operand" "=r,r")
	(compare:CC (match_operand:DF 1 "register_operand" "r,x")
		    (float_extend:DF
		     (match_operand:SF 2 "register_operand" "r,x"))))]
  ""
  "fcmp.sds %0,%1,%2"
  [(set_attr "type" "dpcmp")])

(define_insn ""
  [(set (match_operand:CC 0 "register_operand" "=r,r")
	(compare:CC (float_extend:DF
		     (match_operand:SF 1 "register_operand" "r,x"))
		    (match_operand:DF 2 "register_operand" "r,x")))]
  ""
  "fcmp.ssd %0,%1,%2"
  [(set_attr "type" "dpcmp")])

(define_insn ""
  [(set (match_operand:CC 0 "register_operand" "=r,r,r,r")
	(compare:CC (match_operand:DF 1 "register_operand" "r,r,x,x")
		    (match_operand:DF 2 "real_or_0_operand" "r,G,x,G")))]
  ""
  "@
   fcmp.sdd %0,%1,%2
   fcmp.sds %0,%1,%#r0
   fcmp.sdd %0,%1,%2
   fcmp.sds %0,%1,%#x0"
  [(set_attr "type" "dpcmp")])

;; Store condition code insns.  The compare insns set a register
;; rather than cc0 and record that register for use here.  See above
;; for the special treatment of cmpsi with a constant operand.

;; @@ For the m88110, use fcmpu for bxx sxx inequality comparisons.

(define_expand "seq"
  [(set (match_operand:SI 0 "register_operand" "")
	(match_dup 1))]
  ""
  "operands[1] = emit_test (EQ, SImode);")

(define_expand "sne"
  [(set (match_operand:SI 0 "register_operand" "")
	(match_dup 1))]
  ""
  "operands[1] = emit_test (NE, SImode);")

(define_expand "sgt"
  [(set (match_operand:SI 0 "register_operand" "")
	(match_dup 1))]
  ""
  "operands[1] = emit_test (GT, SImode);")

(define_expand "sgtu"
  [(set (match_operand:SI 0 "register_operand" "")
	(match_dup 1))]
  ""
  "operands[1] = emit_test (GTU, SImode);")

(define_expand "slt"
  [(set (match_operand:SI 0 "register_operand" "")
	(match_dup 1))]
  ""
  "operands[1] = emit_test (LT, SImode);")

(define_expand "sltu"
  [(set (match_operand:SI 0 "register_operand" "")
	(match_dup 1))]
  ""
  "operands[1] = emit_test (LTU, SImode);")

(define_expand "sge"
  [(set (match_operand:SI 0 "register_operand" "")
	(match_dup 1))]
  ""
  "operands[1] = emit_test (GE, SImode);")

(define_expand "sgeu"
  [(set (match_operand:SI 0 "register_operand" "")
	(match_dup 1))]
  ""
  "operands[1] = emit_test (GEU, SImode);")

(define_expand "sle"
  [(set (match_operand:SI 0 "register_operand" "")
	(match_dup 1))]
  ""
  "operands[1] = emit_test (LE, SImode);")

(define_expand "sleu"
  [(set (match_operand:SI 0 "register_operand" "")
	(match_dup 1))]
  ""
  "operands[1] = emit_test (LEU, SImode);")

;; The actual set condition code instruction.

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r")
	(match_operator:SI 1 "relop"
			   [(match_operand:CC 2 "register_operand" "r")
			    (const_int 0)]))]
  ""
  "ext %0,%2,1<%C1>"
  [(set_attr "type" "bit")])

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r")
	(match_operator:SI 1 "even_relop"
			   [(match_operand:CCEVEN 2 "register_operand" "r")
			    (const_int 0)]))]
  ""
  "ext %0,%2,1<%C1>"
  [(set_attr "type" "bit")])

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r")
	(not:SI (match_operator:SI 1 "odd_relop"
			   [(match_operand:CCEVEN 2 "register_operand" "r")
			    (const_int 0)])))]
  ""
  "ext %0,%2,1<%!%C1>"
  [(set_attr "type" "bit")])

(define_split
  [(set (match_operand:SI 0 "register_operand" "=r")
	(match_operator:SI 1 "odd_relop"
			   [(match_operand:CCEVEN 2 "register_operand" "r")
			    (const_int 0)]))
   (clobber (match_operand:SI 3 "register_operand" "=r"))]
  ""
  [(set (match_dup 3) (not:SI (match_op_dup 1 [(match_dup 2) (const_int 0)])))
   (set (match_dup 0) (not:SI (match_dup 3)))]
  "")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r")
	(match_operator:SI 1 "odd_relop"
			   [(match_operand:CCEVEN 2 "register_operand" "r")
			    (const_int 0)]))
   (clobber (match_scratch:SI 3 "=r"))]
  ""
  "#")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r")
	(neg:SI
	 (match_operator:SI 1 "relop"
			    [(match_operand:CC 2 "register_operand" "r")
			     (const_int 0)])))]
  ""
  "extu %0,%2,1<%C1>"
  [(set_attr "type" "bit")])

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r")
	(neg:SI
	 (match_operator:SI 1 "even_relop"
			    [(match_operand:CCEVEN 2 "register_operand" "r")
			     (const_int 0)])))]
  ""
  "extu %0,%2,1<%C1>"
  [(set_attr "type" "bit")])

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r")
	(neg:SI
	 (not:SI (match_operator:SI 1 "odd_relop"
			    [(match_operand:CCEVEN 2 "register_operand" "r")
			     (const_int 0)]))))]
  ""
  "extu %0,%2,1<%!%C1>"
  [(set_attr "type" "bit")])

(define_split
  [(set (match_operand:SI 0 "register_operand" "=r")
	(neg:SI (match_operator:SI 1 "odd_relop"
			   [(match_operand:CCEVEN 2 "register_operand" "r")
			    (const_int 0)])))
   (clobber (match_operand:SI 3 "register_operand" "=r"))]
  ""
  [(set (match_dup 3) (neg:SI (not:SI (match_op_dup 1 [(match_dup 2)
                                                       (const_int 0)]))))
   (set (match_dup 0) (xor:SI (match_dup 3) (const_int 1)))]
  "")

(define_insn
 ""
  [(set (match_operand:SI 0 "register_operand" "=r")
	(neg:SI (match_operator:SI 1 "odd_relop"
			   [(match_operand:CCEVEN 2 "register_operand" "r")
			    (const_int 0)])))
   (clobber (match_scratch:SI 3 "=r"))]
  ""
  "#")




;; Conditional branch insns.  The compare insns set a register
;; rather than cc0 and record that register for use here.  See above
;; for the special case of cmpsi with a constant operand.

(define_expand "bcnd"
  [(set (pc)
	(if_then_else (match_operand 0 "" "")
		      (label_ref (match_operand 1 "" ""))
		      (pc)))]
  ""
  "if (m88k_compare_reg) abort ();")

(define_expand "bxx"
  [(set (pc)
	(if_then_else (match_operand 0 "" "")
		      (label_ref (match_operand 1 "" ""))
		      (pc)))]
  ""
  "if (m88k_compare_reg == 0) abort ();")

(define_expand "beq"
  [(set (pc)
	(if_then_else (eq (match_dup 1) (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "if (m88k_compare_reg == 0)
     {
       emit_bcnd (EQ, operands[0]);
       DONE;
     }
   operands[1] = m88k_compare_reg;")

(define_expand "bne"
  [(set (pc)
	(if_then_else (ne (match_dup 1) (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "if (m88k_compare_reg == 0)
     {
       emit_bcnd (NE, operands[0]);
       DONE;
     }
   operands[1] = m88k_compare_reg;")

(define_expand "bgt"
  [(set (pc)
	(if_then_else (gt (match_dup 1) (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "if (m88k_compare_reg == 0)
     {
       emit_bcnd (GT, operands[0]);
       DONE;
     }
   operands[1] = m88k_compare_reg;")

(define_expand "bgtu"
  [(set (pc)
	(if_then_else (gtu (match_dup 1) (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "if (m88k_compare_reg == 0)
     {
       emit_jump_insn (gen_bxx (emit_test (GTU, VOIDmode), operands[0]));
       DONE;
     }
   operands[1] = m88k_compare_reg;")

(define_expand "blt"
  [(set (pc)
	(if_then_else (lt (match_dup 1) (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "if (m88k_compare_reg == 0)
     {
       emit_bcnd (LT, operands[0]);
       DONE;
     }
   operands[1] = m88k_compare_reg;")

(define_expand "bltu"
  [(set (pc)
	(if_then_else (ltu (match_dup 1) (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "if (m88k_compare_reg == 0)
     {
       emit_jump_insn (gen_bxx (emit_test (LTU, VOIDmode), operands[0]));
       DONE;
     }
   operands[1] = m88k_compare_reg;")

(define_expand "bge"
  [(set (pc)
	(if_then_else (ge (match_dup 1) (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "if (m88k_compare_reg == 0)
     {
       emit_bcnd (GE, operands[0]);
       DONE;
     }
   operands[1] = m88k_compare_reg;")

(define_expand "bgeu"
  [(set (pc)
	(if_then_else (geu (match_dup 1) (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "if (m88k_compare_reg == 0)
     {
       emit_jump_insn (gen_bxx (emit_test (GEU, VOIDmode), operands[0]));
       DONE;
     }
   operands[1] = m88k_compare_reg;")

(define_expand "ble"
  [(set (pc)
	(if_then_else (le (match_dup 1) (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "if (m88k_compare_reg == 0)
     {
       emit_bcnd (LE, operands[0]);
       DONE;
     }
   operands[1] = m88k_compare_reg;")

(define_expand "bleu"
  [(set (pc)
	(if_then_else (leu (match_dup 1) (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "if (m88k_compare_reg == 0)
     {
       emit_jump_insn (gen_bxx (emit_test (LEU, VOIDmode), operands[0]));
       DONE;
     }
   operands[1] = m88k_compare_reg;")

;; The actual conditional branch instruction (both directions).  This
;; uses two unusual template patterns, %Rx and %Px.  %Rx is a prefix code
;; for the immediately following condition and reverses the condition iff
;; operand `x' is a LABEL_REF.  %Px does nothing if `x' is PC and outputs
;; the operand if `x' is a LABEL_REF.

(define_insn ""
  [(set (pc) (if_then_else
	      (match_operator 0 "relop"
			      [(match_operand:CC 1 "register_operand" "r")
			       (const_int 0)])
	      (match_operand 2 "pc_or_label_ref" "")
	      (match_operand 3 "pc_or_label_ref" "")))]
  ""
  "*
{
  if (mostly_false_jump (insn, operands[0]))
    return \"bb0%. %R2%C0,%1,%P2%P3\";
  else
    return \"bb1%. %R3%C0,%1,%P2%P3\";
}"
  [(set_attr "type" "branch")])

;;
;; Here branch prediction is sacrificed. To get it back, you need 
;;  - CCODD (CC mode where the ODD bits are valid)
;;  - several define_split that can apply De Morgan's Law.
;;  - transformations between CCEVEN and CCODD modes. 
;;  

(define_insn ""
  [(set (pc) (if_then_else
	      (match_operator 0 "even_relop"
			      [(match_operand:CCEVEN 1 "register_operand" "r")
			       (const_int 0)])
	      (match_operand 2 "pc_or_label_ref" "")
	      (match_operand 3 "pc_or_label_ref" "")))]
  ""
  "bb%L2%. %C0,%1,%P2%P3"
  [(set_attr "type" "branch")])

(define_insn ""
  [(set (pc) (if_then_else
	      (match_operator 0 "odd_relop"
			      [(match_operand:CCEVEN 1 "register_operand" "r")
			       (const_int 0)])
	      (match_operand 2 "pc_or_label_ref" "")
	      (match_operand 3 "pc_or_label_ref" "")))]
  ""
  "bb%L3%. %!%C0,%1,%P2%P3"
  [(set_attr "type" "branch")])

;; Branch conditional on scc values.  These arise from manipulations on
;; compare words above.
;; Are these really used ? 

(define_insn ""
  [(set (pc)
	(if_then_else
	 (ne (match_operator 0 "relop"
			     [(match_operand:CC 1 "register_operand" "r")
			      (const_int 0)])
	     (const_int 0))
	 (match_operand 2 "pc_or_label_ref" "")
	 (match_operand 3 "pc_or_label_ref" "")))]
  ""
  "bb%L2 %C0,%1,%P2%P3"
  [(set_attr "type" "branch")])

(define_insn ""
  [(set (pc)
	(if_then_else
	 (ne (match_operator 0 "even_relop"
			     [(match_operand:CCEVEN 1 "register_operand" "r")
			      (const_int 0)])
	     (const_int 0))
	 (match_operand 2 "pc_or_label_ref" "")
	 (match_operand 3 "pc_or_label_ref" "")))]
  ""
  "bb%L2 %C0,%1,%P2%P3"
  [(set_attr "type" "branch")])

(define_insn ""
  [(set (pc)
	(if_then_else
	 (ne (match_operator 0 "odd_relop"
			     [(match_operand:CCEVEN 1 "register_operand" "r")
			      (const_int 0)])
	     (const_int 0))
	 (match_operand 2 "pc_or_label_ref" "")
	 (match_operand 3 "pc_or_label_ref" "")))]
  ""
  "bb%L3 %!%C0,%1,%P2%P3"
  [(set_attr "type" "branch")])

(define_insn ""
  [(set (pc)
	(if_then_else
	 (eq (match_operator 0 "relop"
			     [(match_operand:CC 1 "register_operand" "r")
			      (const_int 0)])
	     (const_int 0))
	 (match_operand 2 "pc_or_label_ref" "")
	 (match_operand 3 "pc_or_label_ref" "")))]
  ""
  "bb%L3 %C0,%1,%P2%P3"
  [(set_attr "type" "branch")])

(define_insn ""
  [(set (pc)
	(if_then_else
	 (eq (match_operator 0 "even_relop"
			     [(match_operand:CCEVEN 1 "register_operand" "r")
			      (const_int 0)])
	     (const_int 0))
	 (match_operand 2 "pc_or_label_ref" "")
	 (match_operand 3 "pc_or_label_ref" "")))]
  ""
  "bb%L3 %C0,%1,%P2%P3"
  [(set_attr "type" "branch")])

(define_insn ""
  [(set (pc)
	(if_then_else
	 (eq (match_operator 0 "odd_relop"
			     [(match_operand:CCEVEN 1 "register_operand" "r")
			      (const_int 0)])
	     (const_int 0))
	 (match_operand 2 "pc_or_label_ref" "")
	 (match_operand 3 "pc_or_label_ref" "")))]
  ""
  "bb%L2 %!%C0,%1,%P2%P3"
  [(set_attr "type" "branch")])

(define_insn "locate1"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(high:SI (unspec:SI [(label_ref (match_operand 1 "" ""))] 0)))]
  ""
  "or.u %0,%#r0,%#hi16(%1#abdiff)")

(define_insn "locate2"
  [(parallel [(set (reg:SI 1) (pc))
	      (set (match_operand:SI 0 "register_operand" "=r")
		   (lo_sum:SI (match_dup 0)
			      (unspec:SI
			       [(label_ref (match_operand 1 "" ""))] 0)))])]
  ""
  "bsr.n %1\;or %0,%0,%#lo16(%1#abdiff)\\n%1:"
  [(set_attr "length" "2")])

;; SImode move instructions

(define_expand "movsi"
  [(set (match_operand:SI 0 "general_operand" "")
	(match_operand:SI 1 "general_operand" ""))]
  ""
  "
{
  if (emit_move_sequence (operands, SImode, 0))
    DONE;
}")

(define_expand "reload_insi"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(match_operand:SI 1 "general_operand" ""))
   (clobber (match_operand:SI 2 "register_operand" "=&r"))]
  ""
  "
{
  if (emit_move_sequence (operands, SImode, operands[2]))
    DONE;

  /* We don't want the clobber emitted, so handle this ourselves.  */
  emit_insn (gen_rtx_SET (VOIDmode, operands[0], operands[1]));
  DONE;
}")

(define_insn ""
  [(set (match_operand:SI 0 "nonimmediate_operand" "=r,r,m,r,r,r,x,x,x,m")
	(match_operand:SI 1 "move_operand" "rI,m,rO,J,M,x,r,x,m,x"))]
  "(register_operand (operands[0], SImode)
    || register_operand (operands[1], SImode)
    || operands[1] == const0_rtx)"
  "@
   or %0,%#r0,%1
   %V1ld\\t %0,%1
   %v0st\\t %r1,%0
   subu %0,%#r0,%n1
   set %0,%#r0,%s1
   mov.s %0,%1
   mov.s %0,%1
   mov %0,%1
   %V1ld\\t %0,%1
   %v0st\\t %1,%0"
  [(set_attr "type" "arith,load,store,arith,bit,mov,mov,mov,load,store")])

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r,r,r,r,r")
	(match_operand:SI 1 "arith32_operand" "rI,J,L,M,n"))]
  ""
  "@
   or %0,%#r0,%1
   subu %0,%#r0,%n1
   or.u %0,%#r0,%X1
   set %0,%#r0,%s1
   or.u %0,%#r0,%X1\;or %0,%0,%x1"
  [(set_attr "type" "arith,arith,arith,bit,marith")])

;; @@ Why the constraint "in"?  Doesn't `i' include `n'?
(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r")
	(lo_sum:SI (match_operand:SI 1 "register_operand" "r")
		   (match_operand:SI 2 "immediate_operand" "in")))]
  ""
  "or %0,%1,%#lo16(%g2)")

;; For PIC, symbol_refs are put inside unspec so that the optimizer won't
;; confuse them with real addresses.
(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r")
	(lo_sum:SI (match_operand:SI 1 "register_operand" "r")
		   (unspec:SI [(match_operand:SI 2 "immediate_operand" "in")] 0)))]
  ""
  "or %0,%1,%#lo16(%g2)"
  ;; Need to set length for this arith insn because operand2
  ;; is not an "arith_operand".
  [(set_attr "length" "1")])

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r")
	(high:SI (match_operand 1 "" "")))]
  ""
  "or.u %0,%#r0,%#hi16(%g1)")

;; For PIC, symbol_refs are put inside unspec so that the optimizer won't
;; confuse them with real addresses.
(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r")
	(high:SI (unspec:SI [(match_operand 1 "" "")] 0)))]
  ""
  "or.u %0,%#r0,%#hi16(%g1)"
  ;; Need to set length for this arith insn because operand2
  ;; is not an arith_operand.
  [(set_attr "length" "1")])

;; HImode move instructions

(define_expand "movhi"
  [(set (match_operand:HI 0 "general_operand" "")
	(match_operand:HI 1 "general_operand" ""))]
  ""
  "
{
  if (emit_move_sequence (operands, HImode, 0))
    DONE;
}")

(define_insn ""
  [(set (match_operand:HI 0 "nonimmediate_operand" "=r,r,m,r")
	(match_operand:HI 1 "move_operand" "rP,m,rO,N"))]
  "(register_operand (operands[0], HImode)
    || register_operand (operands[1], HImode)
    || operands[1] == const0_rtx)"
  "@
   or %0,%#r0,%h1
   %V1ld.hu\\t %0,%1
   %v0st.h\\t %r1,%0
   subu %0,%#r0,%H1"
  [(set_attr "type" "arith,load,store,arith")])

(define_insn ""
  [(set (match_operand:HI 0 "register_operand" "=r")
	(subreg:HI (lo_sum:SI (match_operand:SI 1 "register_operand" "r")
			      (match_operand:SI 2 "immediate_operand" "in")) 0))]
  "!flag_pic"
  "or %0,%1,%#lo16(%2)")

;; QImode move instructions

(define_expand "movqi"
  [(set (match_operand:QI 0 "general_operand" "")
	(match_operand:QI 1 "general_operand" ""))]
  ""
  "
{
  if (emit_move_sequence (operands, QImode, 0))
    DONE;
}")

(define_insn ""
  [(set (match_operand:QI 0 "nonimmediate_operand" "=r,r,m,r")
	(match_operand:QI 1 "move_operand" "rP,m,rO,N"))]
  "(register_operand (operands[0], QImode)
    || register_operand (operands[1], QImode)
    || operands[1] == const0_rtx)"
  "@
   or %0,%#r0,%q1
   %V1ld.bu\\t %0,%1
   %v0st.b\\t %r1,%0
   subu %r0,%#r0,%Q1"
  [(set_attr "type" "arith,load,store,arith")])

(define_insn ""
  [(set (match_operand:QI 0 "register_operand" "=r")
	(subreg:QI (lo_sum:SI (match_operand:SI 1 "register_operand" "r")
			      (match_operand:SI 2 "immediate_operand" "in")) 0))]
  "!flag_pic"
  "or %0,%1,%#lo16(%2)")

;; DImode move instructions

(define_expand "movdi"
  [(set (match_operand:DI 0 "general_operand" "")
	(match_operand:DI 1 "general_operand" ""))]
  ""
  "
{
  if (emit_move_sequence (operands, DImode, 0))
    DONE;
}")

(define_insn ""
  [(set (match_operand:DI 0 "register_operand" "=r,x")
	(const_int 0))]
  ""
  "@
   or %0,%#r0,0\;or %d0,%#r0,0
   mov %0,%#x0"
  [(set_attr "type" "marith,mov")])

(define_insn ""
  [(set (match_operand:DI 0 "nonimmediate_operand" "=r,r,m,r,x,x,x,m")
	(match_operand:DI 1 "nonimmediate_operand" "r,m,r,x,r,x,m,x"))]
  ""
  "@
   or %0,%#r0,%1\;or %d0,%#r0,%d1
   %V1ld.d\\t %0,%1
   %v0st.d\\t %1,%0
   mov.d %0,%1
   mov.d %0,%1
   mov %0,%1
   %V1ld.d\\t %0,%1
   %v0st.d\\t %1,%0"
  [(set_attr "type" "marith,loadd,store,mov,mov,mov,loadd,store")])

(define_insn ""
  [(set (match_operand:DI 0 "register_operand" "=r")
	(subreg:DI (lo_sum:SI (match_operand:SI 1 "register_operand" "r")
			      (match_operand:SI 2 "immediate_operand" "in")) 0))]
  "!flag_pic"
  "or %0,%1,%#lo16(%2)")

(define_insn ""
  [(set (match_operand:DI 0 "register_operand" "=r")
	(match_operand:DI 1 "immediate_operand" "n"))]
   ""
   "* return output_load_const_dimode (operands);"
  [(set_attr "type" "marith")
   (set_attr "length" "4")]) ; length is 2, 3 or 4.

;; DFmode move instructions

(define_expand "movdf"
  [(set (match_operand:DF 0 "general_operand" "")
	(match_operand:DF 1 "general_operand" ""))]
  ""
  "
{
  if (emit_move_sequence (operands, DFmode, 0))
    DONE;
}")

(define_split
  [(set (match_operand:DF 0 "register_operand" "=r")
	(match_operand:DF 1 "register_operand" "r"))]
  "reload_completed
   && GET_CODE (operands[0]) == REG && !XRF_REGNO_P (REGNO (operands[0]))
   && GET_CODE (operands[1]) == REG && !XRF_REGNO_P (REGNO (operands[1]))"
  [(set (match_dup 2) (match_dup 3))
   (set (match_dup 4) (match_dup 5))]
  "
{ operands[2] = operand_subword (operands[0], 0, 0, DFmode);
  operands[3] = operand_subword (operands[1], 0, 0, DFmode);
  operands[4] = operand_subword (operands[0], 1, 0, DFmode);
  operands[5] = operand_subword (operands[1], 1, 0, DFmode); }")

;; @@ This pattern is incomplete and doesn't appear necessary.
;;
;; This pattern forces (set (reg:DF ...) (const_double ...))
;; to be reloaded by putting the constant into memory.
;; It must come before the more general movdf pattern.

;(define_insn ""
;  [(set (match_operand:DF 0 "general_operand" "=r,o")
;	(match_operand:DF 1 "" "G,G"))]
;  "GET_CODE (operands[1]) == CONST_DOUBLE"
;  "*
;{
;  switch (which_alternative)
;    {
;    case 0:
;      return \"or %0,%#r0,0\;or %d0,%#r0,0\";
;    case 1:
;      operands[1] = adj_offsettable_operand (operands[0], 4);
;      return \"%v0st\\t %#r0,%0\;st %#r0,%1\";
;    }
;}")

(define_insn ""
  [(set (match_operand:DF 0 "register_operand" "=r,x")
	(const_int 0))]
  ""
  "@
   or %0,%#r0,0\;or %d0,%#r0,0
   mov %0,%#x0"
  [(set_attr "type" "marith,mov")])

(define_insn ""
  [(set (match_operand:DF 0 "nonimmediate_operand" "=r,r,m,x,r,x,x,m")
	(match_operand:DF 1 "nonimmediate_operand" "r,m,r,r,x,x,m,x"))]
  ""
  "@
   or %0,%#r0,%1\;or %d0,%#r0,%d1
   %V1ld.d\\t %0,%1
   %v0st.d\\t %1,%0
   mov.d %0,%1
   mov.d %0,%1
   mov %0,%1
   %V1ld.d\\t %0,%1
   %v0st.d\\t %1,%0"
  [(set_attr "type" "marith,loadd,store,mov,mov,mov,loadd,store")])

(define_insn ""
  [(set (match_operand:DF 0 "register_operand" "=r")
	(subreg:DF (lo_sum:SI (match_operand:SI 1 "register_operand" "r")
			      (match_operand:SI 2 "immediate_operand" "in")) 0))]
  "!flag_pic"
  "or %0,%1,%#lo16(%2)")

(define_insn ""
  [(set (match_operand:DF 0 "register_operand" "=r")
	(match_operand:DF 1 "immediate_operand" "F"))]
   ""
   "* return output_load_const_double (operands);"
  [(set_attr "type" "marith")
   (set_attr "length" "4")]) ; length is 2, 3, or 4.

;; SFmode move instructions

(define_expand "movsf"
  [(set (match_operand:SF 0 "general_operand" "")
	(match_operand:SF 1 "general_operand" ""))]
  ""
  "
{
  if (emit_move_sequence (operands, SFmode, 0))
    DONE;
}")

;; @@ What happens to fconst0_rtx?
(define_insn ""
  [(set (match_operand:SF 0 "register_operand" "=r,x")
	(const_int 0))]
  ""
  "@
   or %0,%#r0,0
   mov %0,%#x0"
  [(set_attr "type" "arith,mov")])

(define_insn ""
  [(set (match_operand:SF 0 "nonimmediate_operand" "=r,r,m,x,r,x,x,m")
	(match_operand:SF 1 "nonimmediate_operand" "r,m,r,r,x,x,m,x"))]
  ""
  "@
   or %0,%#r0,%1
   %V1ld\\t %0,%1
   %v0st\\t %r1,%0
   mov.s %0,%1
   mov.s %0,%1
   mov %0,%1
   %V1ld\\t %0,%1
   %v0st\\t %r1,%0"
  [(set_attr "type" "arith,load,store,mov,mov,mov,load,store")])

(define_insn ""
  [(set (match_operand:SF 0 "register_operand" "=r")
	(subreg:SF (lo_sum:SI (match_operand:SI 1 "register_operand" "r")
			      (match_operand:SI 2 "immediate_operand" "in")) 0))]
  "!flag_pic"
  "or %0,%1,%#lo16(%2)")

(define_insn ""
  [(set (match_operand:SF 0 "register_operand" "=r")
	(match_operand:SF 1 "immediate_operand" "F"))]
  "operands[1] != const0_rtx"
  "* return output_load_const_float (operands);"
  [(set_attr "type" "marith")]) ; length is 1 or 2.

;; String/block move insn.  See m88k.c for details.

(define_expand "movstrsi"
  [(parallel [(set (mem:BLK (match_operand:BLK 0 "" ""))
		   (mem:BLK (match_operand:BLK 1 "" "")))
	      (use (match_operand:SI 2 "arith32_operand" ""))
	      (use (match_operand:SI 3 "immediate_operand" ""))])]
  ""
  "
{
  rtx dest_mem = operands[0];
  rtx src_mem = operands[1];
  operands[0] = copy_to_mode_reg (SImode, XEXP (operands[0], 0));
  operands[1] = copy_to_mode_reg (SImode, XEXP (operands[1], 0));
  expand_block_move (dest_mem, src_mem, operands);
  DONE;
}")

;; ??? We shouldn't be allowing such mode mismatches
(define_insn ""
  [(set (match_operand 0 "register_operand" "=r")
	(match_operand:BLK 1 "memory_operand" "m"))]
  ""
  "*
{
  switch (GET_MODE (operands[0]))
    {
    case QImode:
      return \"%V1ld.bu\\t %0,%1\";
    case HImode:
      return \"%V1ld.hu\\t %0,%1\";
    case SImode:
      return \"%V1ld\\t %0,%1\";
    case DImode:
      return \"%V1ld.d\\t %0,%1\";
    default:
      abort ();
    }
}"
  [(set_attr "type" "load")])

(define_insn ""
  [(set (match_operand:BLK 0 "memory_operand" "=m")
	(match_operand 1 "register_operand" "r"))]
  ""
  "*
{
  switch (GET_MODE (operands[1]))
    {
    case QImode:
      return \"%v0st.b\\t %1,%0\";
    case HImode:
      return \"%v0st.h\\t %1,%0\";
    case SImode:
      return \"%v0st\\t %1,%0\";
    case DImode:
      return \"%v0st.d\\t %1,%0\";
    default:
      abort ();
    }
}"
  [(set_attr "type" "store")])

;; Call a non-looping block move library function (e.g. __movstrSI96x64).
;; operand 0 is the function name
;; operand 1 is the destination pointer
;; operand 2 is the source pointer
;; operand 3 is the offset for the source and destination pointers
;; operand 4 is the first value to be loaded
;; operand 5 is the register to hold the value (r4 or r5)

(define_expand "call_block_move"
  [(set (reg:SI 3) (minus:SI (match_operand:SI 2 "register_operand" "")
			     (match_operand:SI 3 "immediate_operand" "")))
   (set (match_operand 5 "register_operand" "")
	(match_operand 4 "memory_operand" ""))
   (set (reg:SI 2) (minus:SI (match_operand:SI 1 "register_operand" "")
			     (match_dup 3)))
   (use (reg:SI 2))
   (use (reg:SI 3))
   (use (match_dup 5))
   (parallel [(set (reg:DI 2)
		   (call (mem:SI (match_operand 0 "" ""))
			 (const_int 0)))
	      (clobber (reg:SI 1))])]
  ""
  "")

;; Call an SImode looping block move library function (e.g. __movstrSI64n68).
;; operands 0-5 as in the non-looping interface
;; operand 6 is the loop count

(define_expand "call_movstrsi_loop"
  [(set (reg:SI 3) (minus:SI (match_operand:SI 2 "register_operand" "")
			     (match_operand:SI 3 "immediate_operand" "")))
   (set (match_operand:SI 5 "register_operand" "")
	(match_operand 4 "memory_operand" ""))
   (set (reg:SI 2) (minus:SI (match_operand:SI 1 "register_operand" "")
			     (match_dup 3)))
   (set (reg:SI 6) (match_operand:SI 6 "immediate_operand" ""))
   (use (reg:SI 2))
   (use (reg:SI 3))
   (use (match_dup 5))
   (use (reg:SI 6))
   (parallel [(set (reg:DI 2)
		   (call (mem:SI (match_operand 0 "" ""))
			 (const_int 0)))
	      (clobber (reg:SI 1))])]
  ""
  "")

;;- zero extension instructions

(define_expand "zero_extendhisi2"
  [(set (match_operand:SI 0 "register_operand" "")
	(zero_extend:SI (match_operand:HI 1 "nonimmediate_operand" "")))]
  ""
  "
{
  if (GET_CODE (operands[1]) == MEM
      && symbolic_address_p (XEXP (operands[1], 0)))
    operands[1]
      = legitimize_address (flag_pic, operands[1], 0, 0);
}")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r,r,r")
	(zero_extend:SI (match_operand:HI 1 "move_operand" "!r,n,m")))]
  "GET_CODE (operands[1]) != CONST_INT"
  "@
   mask %0,%1,0xffff
   or %0,%#r0,%h1
   %V1ld.hu\\t %0,%1"
  [(set_attr "type" "arith,arith,load")])

(define_expand "zero_extendqihi2"
  [(set (match_operand:HI 0 "register_operand" "")
	(zero_extend:HI (match_operand:QI 1 "nonimmediate_operand" "")))]
  ""
  "
{
  if (GET_CODE (operands[1]) == MEM
      && symbolic_address_p (XEXP (operands[1], 0)))
    operands[1]
      = legitimize_address (flag_pic, operands[1], 0, 0);
}")

(define_insn ""
  [(set (match_operand:HI 0 "register_operand" "=r,r,r")
	(zero_extend:HI (match_operand:QI 1 "move_operand" "r,n,m")))]
  "GET_CODE (operands[1]) != CONST_INT"
  "@
   mask %0,%1,0xff
   or %0,%#r0,%q1
   %V1ld.bu\\t %0,%1"
  [(set_attr "type" "arith,arith,load")])

(define_expand "zero_extendqisi2"
  [(set (match_operand:SI 0 "register_operand" "")
	(zero_extend:SI (match_operand:QI 1 "nonimmediate_operand" "")))]
  ""
  "
{
  if (GET_CODE (operands[1]) == MEM
      && symbolic_address_p (XEXP (operands[1], 0)))
    {
      operands[1]
	= legitimize_address (flag_pic, operands[1], 0, 0);
      emit_insn (gen_rtx_SET (VOIDmode, operands[0],
			      gen_rtx_ZERO_EXTEND (SImode, operands[1])));
      DONE;
    }
}")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r,r,r")
	(zero_extend:SI (match_operand:QI 1 "move_operand" "r,n,m")))]
  "GET_CODE (operands[1]) != CONST_INT"
  "@
   mask %0,%1,0xff
   or %0,%#r0,%q1
   %V1ld.bu\\t %0,%1"
  [(set_attr "type" "arith,arith,load")])

;;- sign extension instructions

(define_expand "extendsidi2"
  [(set (subreg:SI (match_operand:DI 0 "register_operand" "=r") 1)
        (match_operand:SI 1 "general_operand" "g"))
   (set (subreg:SI (match_dup 0) 0)
        (ashiftrt:SI (subreg:SI (match_dup 0) 1)
                     (const_int 31)))]
  ""
  "")

(define_expand "extendhisi2"
  [(set (match_operand:SI 0 "register_operand" "")
	(sign_extend:SI (match_operand:HI 1 "nonimmediate_operand" "")))]
  ""
  "
{
  if (GET_CODE (operands[1]) == MEM
      && symbolic_address_p (XEXP (operands[1], 0)))
    operands[1]
      = legitimize_address (flag_pic, operands[1], 0, 0);
}")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r,r,r,r")
	(sign_extend:SI (match_operand:HI 1 "move_operand" "!r,P,N,m")))]
  "GET_CODE (operands[1]) != CONST_INT"
  "@
   ext %0,%1,16<0>
   or %0,%#r0,%h1
   subu %0,%#r0,%H1
   %V1ld.h\\t %0,%1"
  [(set_attr "type" "bit,arith,arith,load")])

(define_expand "extendqihi2"
  [(set (match_operand:HI 0 "register_operand" "")
	(sign_extend:HI (match_operand:QI 1 "nonimmediate_operand" "")))]
  ""
  "
{
  if (GET_CODE (operands[1]) == MEM
      && symbolic_address_p (XEXP (operands[1], 0)))
    operands[1]
      = legitimize_address (flag_pic, operands[1], 0, 0);
}")

(define_insn ""
  [(set (match_operand:HI 0 "register_operand" "=r,r,r,r")
	(sign_extend:HI (match_operand:QI 1 "move_operand" "!r,P,N,m")))]
  "GET_CODE (operands[1]) != CONST_INT"
  "@
   ext %0,%1,8<0>
   or %0,%#r0,%q1
   subu %0,%#r0,%Q1
   %V1ld.b\\t %0,%1"
  [(set_attr "type" "bit,arith,arith,load")])

(define_expand "extendqisi2"
  [(set (match_operand:SI 0 "register_operand" "")
	(sign_extend:SI (match_operand:QI 1 "nonimmediate_operand" "")))]
  ""
  "
{
  if (GET_CODE (operands[1]) == MEM
      && symbolic_address_p (XEXP (operands[1], 0)))
    operands[1]
      = legitimize_address (flag_pic, operands[1], 0, 0);
}")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r,r,r,r")
	(sign_extend:SI (match_operand:QI 1 "move_operand" "!r,P,N,m")))]
  "GET_CODE (operands[1]) != CONST_INT"
  "@
   ext %0,%1,8<0>
   or %0,%#r0,%q1
   subu %0,%#r0,%Q1
   %V1ld.b\\t %0,%1"
  [(set_attr "type" "bit,arith,arith,load")])

;; Conversions between float and double.

;; The fadd instruction does not conform to IEEE 754 when used to
;; convert between float and double.  In particular, the sign of -0 is
;; not preserved.  Interestingly, fsub does conform.

(define_expand "extendsfdf2"
  [(set (match_operand:DF 0 "register_operand" "=r")
	(float_extend:DF (match_operand:SF 1 "register_operand" "r")))]
  ""
  "")

(define_insn ""
  [(set (match_operand:DF 0 "register_operand" "=r")
	(float_extend:DF (match_operand:SF 1 "register_operand" "r")))]
  "! TARGET_88110"
  "fsub.dss %0,%1,%#r0"
  [(set_attr "type" "spadd")])

(define_insn ""
  [(set (match_operand:DF 0 "register_operand" "=r,x")
	(float_extend:DF (match_operand:SF 1 "register_operand" "r,x")))]
  "TARGET_88110"
  "fcvt.ds %0,%1"
  [(set_attr "type" "spadd")])

(define_expand "truncdfsf2"
  [(set (match_operand:SF 0 "register_operand" "=r")
	(float_truncate:SF (match_operand:DF 1 "register_operand" "r")))]
  ""
  "")

(define_insn ""
  [(set (match_operand:SF 0 "register_operand" "=r")
	(float_truncate:SF (match_operand:DF 1 "register_operand" "r")))]
  "! TARGET_88110"
  "fsub.sds %0,%1,%#r0"
  [(set_attr "type" "dpadd")])

(define_insn ""
  [(set (match_operand:SF 0 "register_operand" "=r,x")
	(float_truncate:SF (match_operand:DF 1 "register_operand" "r,x")))]
  "TARGET_88110"
  "fcvt.sd %0,%1"
  [(set_attr "type" "dpadd")])

;; Conversions between floating point and integer

(define_insn "floatsidf2"
  [(set (match_operand:DF 0 "register_operand" "=r,x")
	(float:DF (match_operand:SI 1 "register_operand" "r,r")))]
  ""
  "flt.ds %0,%1"
  [(set_attr "type" "spadd,dpadd")])

(define_insn "floatsisf2"
  [(set (match_operand:SF 0 "register_operand" "=r,x")
	(float:SF (match_operand:SI 1 "register_operand" "r,r")))]
  ""
  "flt.ss %0,%1"
  [(set_attr "type" "spadd,spadd")])

(define_insn "fix_truncdfsi2"
  [(set (match_operand:SI 0 "register_operand" "=r,r")
	(fix:SI (match_operand:DF 1 "register_operand" "r,x")))]
  ""
  "trnc.sd %0,%1"
  [(set_attr "type" "dpadd,dpadd")])

(define_insn "fix_truncsfsi2"
  [(set (match_operand:SI 0 "register_operand" "=r,r")
	(fix:SI (match_operand:SF 1 "register_operand" "r,x")))]
  ""
  "trnc.ss %0,%1"
  [(set_attr "type" "spadd,dpadd")])


;;- arithmetic instructions
;;- add instructions

(define_insn "addsi3"
  [(set (match_operand:SI 0 "register_operand" "=r,r")
	(plus:SI (match_operand:SI 1 "add_operand" "%r,r")
		 (match_operand:SI 2 "add_operand" "rI,J")))]
  ""
  "@
   addu %0,%1,%2
   subu %0,%1,%n2")

;; patterns for mixed mode floating point.
;; Do not define patterns that utilize mixed mode arithmetic that result
;; in narrowing the precision, because it loses accuracy, since the standard
;; requires double rounding, whereas the 88000 instruction only rounds once.

(define_expand "adddf3"
  [(set (match_operand:DF 0 "register_operand" "=r,x")
	(plus:DF (match_operand:DF 1 "general_operand" "%r,x")
		 (match_operand:DF 2 "general_operand" "r,x")))]
  ""
  "
{
  operands[1] = legitimize_operand (operands[1], DFmode);
  operands[2] = legitimize_operand (operands[2], DFmode);
}")

(define_insn ""
  [(set (match_operand:DF 0 "register_operand" "=r,x")
	(plus:DF (float_extend:DF (match_operand:SF 1 "register_operand" "r,x"))
		 (float_extend:DF (match_operand:SF 2 "register_operand" "r,x"))))]
  ""
  "fadd.dss %0,%1,%2"
  [(set_attr "type" "spadd")])

(define_insn ""
  [(set (match_operand:DF 0 "register_operand" "=r,x")
	(plus:DF (match_operand:DF 1 "register_operand" "r,x")
		 (float_extend:DF (match_operand:SF 2 "register_operand" "r,x"))))]
  ""
  "fadd.dds %0,%1,%2"
  [(set_attr "type" "dpadd")])

(define_insn ""
  [(set (match_operand:DF 0 "register_operand" "=r,x")
	(plus:DF (float_extend:DF (match_operand:SF 1 "register_operand" "r,x"))
		 (match_operand:DF 2 "register_operand" "r,x")))]
  ""
  "fadd.dsd %0,%1,%2"
  [(set_attr "type" "dpadd")])

(define_insn ""
  [(set (match_operand:DF 0 "register_operand" "=r,x")
	(plus:DF (match_operand:DF 1 "register_operand" "%r,x")
		 (match_operand:DF 2 "register_operand" "r,x")))]
  ""
  "fadd.ddd %0,%1,%2"
  [(set_attr "type" "dpadd")])

(define_insn "addsf3"
  [(set (match_operand:SF 0 "register_operand" "=r,x")
	(plus:SF (match_operand:SF 1 "register_operand" "%r,x")
		 (match_operand:SF 2 "register_operand" "r,x")))]
  ""
  "fadd.sss %0,%1,%2"
  [(set_attr "type" "spadd")])

(define_insn ""
  [(set (match_operand:DI 0 "register_operand" "=r")
	(plus:DI (match_operand:DI 1 "register_operand" "r")
		 (zero_extend:DI
		  (match_operand:SI 2 "register_operand" "r"))))
   (clobber (reg:CC 0))]
  ""
  "addu.co %d0,%d1,%2\;addu.ci %0,%1,%#r0"
  [(set_attr "type" "marith")])

(define_insn ""
  [(set (match_operand:DI 0 "register_operand" "=r")
	(plus:DI (zero_extend:DI
		  (match_operand:SI 1 "register_operand" "r"))
		 (match_operand:DI 2 "register_operand" "r")))
   (clobber (reg:CC 0))]
  ""
  "addu.co %d0,%1,%d2\;addu.ci %0,%#r0,%2"
  [(set_attr "type" "marith")])

(define_insn "adddi3"
  [(set (match_operand:DI 0 "register_operand" "=r")
	(plus:DI (match_operand:DI 1 "register_operand" "%r")
		 (match_operand:DI 2 "register_operand" "r")))
   (clobber (reg:CC 0))]
  ""
  "addu.co %d0,%d1,%d2\;addu.ci %0,%1,%2"
  [(set_attr "type" "marith")])

;; Add with carry insns.

(define_insn ""
  [(parallel [(set (match_operand:SI 0 "register_operand" "=r")
		   (plus:SI (match_operand:SI 1 "reg_or_0_operand" "rO")
			    (match_operand:SI 2 "reg_or_0_operand" "rO")))
	      (set (reg:CC 0)
		   (unspec:CC [(match_dup 1) (match_dup 2)] 0))])]
  ""
  "addu.co %r0,%r1,%r2")

(define_insn ""
  [(set (reg:CC 0) (unspec:CC [(match_operand:SI 0 "reg_or_0_operand" "rO")
			       (match_operand:SI 1 "reg_or_0_operand" "rO")]
			      0))]
  ""
  "addu.co %#r0,%r0,%r1")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r")
	(plus:SI (match_operand:SI 1 "reg_or_0_operand" "rO")
		 (unspec:SI [(match_operand:SI 2 "reg_or_0_operand" "rO")
			     (reg:CC 0)] 0)))]
  ""
  "addu.ci %r0,%r1,%r2")

;;- subtract instructions

(define_insn "subsi3"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(minus:SI (match_operand:SI 1 "register_operand" "r")
		  (match_operand:SI 2 "arith32_operand" "rI")))]
  ""
  "subu %0,%1,%2")

;; patterns for mixed mode floating point
;; Do not define patterns that utilize mixed mode arithmetic that result
;; in narrowing the precision, because it loses accuracy, since the standard
;; requires double rounding, whereas the 88000 instruction only rounds once.

(define_expand "subdf3"
  [(set (match_operand:DF 0 "register_operand" "=r,x")
	(minus:DF (match_operand:DF 1 "general_operand" "r,x")
		  (match_operand:DF 2 "general_operand" "r,x")))]
  ""
  "
{
  operands[1] = legitimize_operand (operands[1], DFmode);
  operands[2] = legitimize_operand (operands[2], DFmode);
}")

(define_insn ""
  [(set (match_operand:DF 0 "register_operand" "=r,x")
	(minus:DF (float_extend:DF (match_operand:SF 1 "register_operand" "r,x"))
		  (float_extend:DF (match_operand:SF 2 "register_operand" "r,x"))))]
  ""
  "fsub.dss %0,%1,%2"
  [(set_attr "type" "spadd")])

(define_insn ""
  [(set (match_operand:DF 0 "register_operand" "=r,x")
	(minus:DF (match_operand:DF 1 "register_operand" "r,x")
		  (float_extend:DF (match_operand:SF 2 "register_operand" "r,x"))))]
  ""
  "fsub.dds %0,%1,%2"
  [(set_attr "type" "dpadd")])

(define_insn ""
  [(set (match_operand:DF 0 "register_operand" "=r,x")
	(minus:DF (float_extend:DF (match_operand:SF 1 "register_operand" "r,x"))
		  (match_operand:DF 2 "register_operand" "r,x")))]
  ""
  "fsub.dsd %0,%1,%2"
  [(set_attr "type" "dpadd")])

(define_insn ""
  [(set (match_operand:DF 0 "register_operand" "=r,x")
	(minus:DF (match_operand:DF 1 "register_operand" "r,x")
		  (match_operand:DF 2 "register_operand" "r,x")))]
  ""
  "fsub.ddd %0,%1,%2"
  [(set_attr "type" "dpadd")])

(define_insn "subsf3"
  [(set (match_operand:SF 0 "register_operand" "=r,x")
	(minus:SF (match_operand:SF 1 "register_operand" "r,x")
		  (match_operand:SF 2 "register_operand" "r,x")))]
  ""
  "fsub.sss %0,%1,%2"
  [(set_attr "type" "spadd")])

(define_insn ""
  [(set (match_operand:DI 0 "register_operand" "=r")
	(minus:DI (match_operand:DI 1 "register_operand" "r")
		  (zero_extend:DI
		   (match_operand:SI 2 "register_operand" "r"))))
   (clobber (reg:CC 0))]
  ""
  "subu.co %d0,%d1,%2\;subu.ci %0,%1,%#r0"
  [(set_attr "type" "marith")])

(define_insn ""
  [(set (match_operand:DI 0 "register_operand" "=r")
	(minus:DI (zero_extend:DI
		   (match_operand:SI 1 "register_operand" "r"))
		  (match_operand:DI 2 "register_operand" "r")))
   (clobber (reg:CC 0))]
  ""
  "subu.co %d0,%1,%d2\;subu.ci %0,%#r0,%2"
  [(set_attr "type" "marith")])

(define_insn "subdi3"
  [(set (match_operand:DI 0 "register_operand" "=r")
	(minus:DI (match_operand:DI 1 "register_operand" "r")
		  (match_operand:DI 2 "register_operand" "r")))
   (clobber (reg:CC 0))]
  ""
  "subu.co %d0,%d1,%d2\;subu.ci %0,%1,%2"
  [(set_attr "type" "marith")])

;; Subtract with carry insns.

(define_insn ""
  [(parallel [(set (match_operand:SI 0 "register_operand" "=r")
		   (minus:SI (match_operand:SI 1 "reg_or_0_operand" "rO")
			     (match_operand:SI 2 "reg_or_0_operand" "rO")))
	      (set (reg:CC 0)
		   (unspec:CC [(match_dup 1) (match_dup 2)] 1))])]
  ""
  "subu.co %r0,%r1,%r2")

(define_insn ""
  [(set (reg:CC 0) (unspec:CC [(match_operand:SI 0 "reg_or_0_operand" "rO")
			       (match_operand:SI 1 "reg_or_0_operand" "rO")]
			      1))]
  ""
  "subu.co %#r0,%r0,%r1")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r")
	(minus:SI (match_operand:SI 1 "reg_or_0_operand" "rO")
		  (unspec:SI [(match_operand:SI 2 "reg_or_0_operand" "rO")
			      (reg:CC 0)] 1)))]
  ""
  "subu.ci %r0,%r1,%r2")

;;- multiply instructions
;;
;; There is an unfounded silicon errata for E.1 requiring that an
;; immediate constant value in div/divu/mul instructions be less than
;; 0x800.  This is no longer provided for.

(define_insn "mulsi3"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(mult:SI (match_operand:SI 1 "arith32_operand" "%r")
		 (match_operand:SI 2 "arith32_operand" "rI")))]
  ""
  "mul %0,%1,%2"
  [(set_attr "type" "imul")])

(define_insn "umulsidi3"
  [(set (match_operand:DI 0 "register_operand" "=r")
        (mult:DI (zero_extend:DI (match_operand:SI 1 "register_operand" "%r"))
                 (zero_extend:DI (match_operand:SI 2 "register_operand" "r"))))]
  "TARGET_88110"
  "mulu.d %0,%1,%2"
  [(set_attr "type" "imul")])

;; patterns for mixed mode floating point
;; Do not define patterns that utilize mixed mode arithmetic that result
;; in narrowing the precision, because it loses accuracy, since the standard
;; requires double rounding, whereas the 88000 instruction only rounds once.

(define_expand "muldf3"
  [(set (match_operand:DF 0 "register_operand" "=r,x")
	(mult:DF (match_operand:DF 1 "general_operand" "%r,x")
		 (match_operand:DF 2 "general_operand" "r,x")))]
  ""
  "
{
  operands[1] = legitimize_operand (operands[1], DFmode);
  operands[2] = legitimize_operand (operands[2], DFmode);
}")

(define_insn ""
  [(set (match_operand:DF 0 "register_operand" "=r,x")
	(mult:DF (float_extend:DF (match_operand:SF 1 "register_operand" "r,x"))
		 (float_extend:DF (match_operand:SF 2 "register_operand" "r,x"))))]
  ""
  "fmul.dss %0,%1,%2"
  [(set_attr "type" "spmul")])

(define_insn ""
  [(set (match_operand:DF 0 "register_operand" "=r,x")
	(mult:DF (match_operand:DF 1 "register_operand" "r,x")
		 (float_extend:DF (match_operand:SF 2 "register_operand" "r,x"))))]
  ""
  "fmul.dds %0,%1,%2"
  [(set_attr "type" "spmul")])

(define_insn ""
  [(set (match_operand:DF 0 "register_operand" "=r,x")
	(mult:DF (float_extend:DF (match_operand:SF 1 "register_operand" "r,x"))
		 (match_operand:DF 2 "register_operand" "r,x")))]
  ""
  "fmul.dsd %0,%1,%2"
  [(set_attr "type" "spmul")])

(define_insn ""
  [(set (match_operand:DF 0 "register_operand" "=r,x")
	(mult:DF (match_operand:DF 1 "register_operand" "%r,x")
		 (match_operand:DF 2 "register_operand" "r,x")))]
  ""
  "fmul.ddd %0,%1,%2"
  [(set_attr "type" "dpmul")])

(define_insn "mulsf3"
  [(set (match_operand:SF 0 "register_operand" "=r,x")
	(mult:SF (match_operand:SF 1 "register_operand" "%r,x")
		 (match_operand:SF 2 "register_operand" "r,x")))]
  ""
  "fmul.sss %0,%1,%2"
  [(set_attr "type" "spmul")])

;;- divide instructions
;;
;; The 88k div and divu instructions don't reliably trap on
;; divide-by-zero.  A trap to vector 503 asserts divide-by-zero.  The
;; general scheme for doing divide is to do a 4-way split based on the
;; sign of the two operand and do the appropriate negates.
;;
;; The conditional trap instruction is not used as this serializes the
;; processor.  Instead a conditional branch and an unconditional trap
;; are used, but after the divu.  Since the divu takes up to 38 cycles,
;; the conditional branch is essentially free.
;;
;; Two target options control how divide is done.  One options selects
;; whether to do the branch and negate scheme instead of using the div
;; instruction; the other option selects whether to explicitly check
;; for divide-by-zero or take your chances.  If the div instruction is
;; used, the O/S must complete the operation if the operands are
;; negative.  The O/S will signal an overflow condition if the most
;; negative number (-2147483648) is divided by negative 1.
;;
;; There is an unfounded silicon errata for E.1 requiring that an
;; immediate constant value in div/divu/mul instructions be less than
;; 0x800.  This is no longer provided for.

;; Division by 0 trap
(define_insn "trap_divide_by_zero"
  [(trap_if (const_int 1) (const_int 503))]
  ""
  "tb0 0,%#r0,503"
  [(set_attr "type" "weird")])

;; Conditional division by 0 trap.
(define_expand "tcnd_divide_by_zero"
  [(set (pc)
	(if_then_else (eq (match_operand:SI 0 "register_operand" "")
			  (const_int 0))
		      (pc)
		      (match_operand 1 "" "")))
   (trap_if (const_int 1) (const_int 503))]
  ""
  "
{
  emit_insn (gen_cmpsi (operands[0], const0_rtx));
  emit_jump_insn (gen_bne (operands[1]));
  emit_insn (gen_trap_divide_by_zero ());
  DONE;
}")

(define_expand "divsi3"
  [(set (match_operand:SI 0 "register_operand" "")
	(div:SI (match_operand:SI 1 "arith32_operand" "")
		(match_operand:SI 2 "arith32_operand" "")))]
  ""
  "
{
  rtx op0 = operands[0];
  rtx op1 = operands[1];
  rtx op2 = operands[2];
  rtx join_label;

  /* @@ This needs to be reworked.  Torbjorn Granlund has suggested making
     it a runtime (perhaps quite special).  */

  if (GET_CODE (op1) == CONST_INT)
    op1 = force_reg (SImode, op1);

  else if (GET_CODE (op2) == CONST_INT
	   && ! SMALL_INT (operands[2]))
    op2 = force_reg (SImode, op2);

  if (op2 == const0_rtx)
    {
      emit_insn (gen_trap_divide_by_zero ());
      emit_insn (gen_dummy (op0));
      DONE;
    }

  if (TARGET_USE_DIV)
    {
      emit_move_insn (op0, gen_rtx_DIV (SImode, op1, op2));
      if (TARGET_CHECK_ZERO_DIV && GET_CODE (op2) != CONST_INT)
	{
	  rtx label = gen_label_rtx ();
	  emit_insn (gen_tcnd_divide_by_zero (op2, label));
	  emit_label (label);
	  emit_insn (gen_dummy (op0));
	}
      DONE;
    }

  join_label = gen_label_rtx ();
  if (GET_CODE (op1) == CONST_INT)
    {
      int neg = FALSE;
      rtx neg_op2 = gen_reg_rtx (SImode);
      rtx label1 = gen_label_rtx ();

      if (INTVAL (op1) < 0)
	{
	  neg = TRUE;
	  op1 = GEN_INT (-INTVAL (op1));
	}
      op1 = force_reg (SImode, op1);

      emit_insn (gen_negsi2 (neg_op2, op2));
      emit_insn (gen_cmpsi (op2, const0_rtx));
      emit_jump_insn (gen_bgt (label1));
						/* constant / 0-or-negative */
      emit_move_insn (op0, gen_rtx_UDIV (SImode, op1, neg_op2));
      if (!neg)
	emit_insn (gen_negsi2 (op0, op0));

      if (TARGET_CHECK_ZERO_DIV)
	emit_insn (gen_tcnd_divide_by_zero (op2, join_label));
      emit_jump_insn (gen_jump (join_label));
      emit_barrier ();

      emit_label (label1);			/* constant / positive */
      emit_move_insn (op0, gen_rtx_UDIV (SImode, op1, op2));
      if (neg)
	emit_insn (gen_negsi2 (op0, op0));
    }

  else if (GET_CODE (op2) == CONST_INT)
    {
      int neg = FALSE;
      rtx neg_op1 = gen_reg_rtx (SImode);
      rtx label1 = gen_label_rtx ();

      if (INTVAL (op2) < 0)
	{
	  neg = TRUE;
	  op2 = GEN_INT (-INTVAL (op2));
	}
      else if (! SMALL_INT (operands[2]))
	op2 = force_reg (SImode, op2);

      emit_insn (gen_negsi2 (neg_op1, op1));
      emit_insn (gen_cmpsi (op1, const0_rtx));
      emit_jump_insn (gen_bge (label1));
						/* 0-or-negative / constant */
      emit_move_insn (op0, gen_rtx_UDIV (SImode, neg_op1, op2));
      if (!neg)
	emit_insn (gen_negsi2 (op0, op0));

      emit_jump_insn (gen_jump (join_label));
      emit_barrier ();

      emit_label (label1);			/* positive / constant */
      emit_move_insn (op0, gen_rtx_UDIV (SImode, op1, op2));
      if (neg)
	emit_insn (gen_negsi2 (op0, op0));
    }

  else
    {
      rtx neg_op1 = gen_reg_rtx (SImode);
      rtx neg_op2 = gen_reg_rtx (SImode);
      rtx label1 = gen_label_rtx ();
      rtx label2 = gen_label_rtx ();
      rtx label3 = gen_label_rtx ();
      rtx label4;

      emit_insn (gen_negsi2 (neg_op2, op2));
      emit_insn (gen_cmpsi (op2, const0_rtx));
      emit_jump_insn (gen_bgt (label1));

      emit_insn (gen_negsi2 (neg_op1, op1));
      emit_insn (gen_cmpsi (op1, const0_rtx));
      emit_jump_insn (gen_bge (label2));
						/* negative / negative-or-0 */
      emit_move_insn (op0, gen_rtx_UDIV (SImode, neg_op1, neg_op2));

      if (TARGET_CHECK_ZERO_DIV)
	{
	  label4 = gen_label_rtx ();
	  emit_insn (gen_cmpsi (op2, const0_rtx));
	  emit_jump_insn (gen_bne (join_label));
	  emit_label (label4);
	  emit_insn (gen_trap_divide_by_zero ());
	}
      emit_jump_insn (gen_jump (join_label));
      emit_barrier ();

      emit_label (label2);			/* pos.-or-0 / neg.-or-0 */
      emit_move_insn (op0, gen_rtx_UDIV (SImode, op1, neg_op2));

      if (TARGET_CHECK_ZERO_DIV)
	{
	  emit_insn (gen_cmpsi (op2, const0_rtx));
	  emit_jump_insn (gen_beq (label4));
	}

      emit_insn (gen_negsi2 (op0, op0));
      emit_jump_insn (gen_jump (join_label));
      emit_barrier ();

      emit_label (label1);
      emit_insn (gen_negsi2 (neg_op1, op1));
      emit_insn (gen_cmpsi (op1, const0_rtx));
      emit_jump_insn (gen_bge (label3));
						/* negative / positive */
      emit_move_insn (op0, gen_rtx_UDIV (SImode, neg_op1, op2));
      emit_insn (gen_negsi2 (op0, op0));
      emit_jump_insn (gen_jump (join_label));
      emit_barrier ();

      emit_label (label3);			/* positive-or-0 / positive */
      emit_move_insn (op0, gen_rtx_UDIV (SImode, op1, op2));
    }

  emit_label (join_label);

  emit_insn (gen_dummy (op0));
  DONE;
}")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r")
	(div:SI (match_operand:SI 1 "register_operand" "r")
		(match_operand:SI 2 "arith_operand" "rI")))]
  ""
  "div %0,%1,%2"
  [(set_attr "type" "idiv")])

(define_expand "udivsi3"
  [(set (match_operand:SI 0 "register_operand" "")
	(udiv:SI (match_operand:SI 1 "register_operand" "")
		 (match_operand:SI 2 "arith32_operand" "")))]
  ""
  "
{
  rtx op2 = operands[2];

  if (op2 == const0_rtx)
    {
      emit_insn (gen_trap_divide_by_zero ());
      emit_insn (gen_dummy (operands[0]));
      DONE;
    }
  else if (GET_CODE (op2) != CONST_INT && TARGET_CHECK_ZERO_DIV)
    {
      rtx label = gen_label_rtx ();
      emit_insn (gen_rtx_SET (VOIDmode, operands[0],
			      gen_rtx_UDIV (SImode, operands[1], op2)));
      emit_insn (gen_tcnd_divide_by_zero (op2, label));
      emit_label (label);
      emit_insn (gen_dummy (operands[0]));
      DONE;
    }
}")

(define_insn ""
 [(set (match_operand:SI 0 "register_operand" "=r")
       (udiv:SI (match_operand:SI 1 "register_operand" "r")
		(match_operand:SI 2 "arith32_operand" "rI")))]
  "operands[2] != const0_rtx"
  "divu %0,%1,%2"
  [(set_attr "type" "idiv")])

(define_insn ""
 [(set (match_operand:SI 0 "register_operand" "=r")
       (udiv:SI (match_operand:SI 1 "register_operand" "r")
		(const_int 0)))]
  ""
  "tb0 0,%#r0,503"
  [(set_attr "type" "weird")])

;; patterns for mixed mode floating point.
;; Do not define patterns that utilize mixed mode arithmetic that result
;; in narrowing the precision, because it loses accuracy, since the standard
;; requires double rounding, whereas the 88000 instruction only rounds once.

(define_expand "divdf3"
  [(set (match_operand:DF 0 "register_operand" "=r,x")
	(div:DF (match_operand:DF 1 "general_operand" "r,x")
		(match_operand:DF 2 "general_operand" "r,x")))]
  ""
  "
{
  operands[1] = legitimize_operand (operands[1], DFmode);
  if (real_power_of_2_operand (operands[2], DFmode))
    {
      union real_extract u;
      bcopy (&CONST_DOUBLE_LOW (operands[2]), &u, sizeof u);
      emit_insn (gen_muldf3 (operands[0], operands[1],
			     CONST_DOUBLE_FROM_REAL_VALUE (1.0/u.d, DFmode)));
      DONE;
    }
  else if (! register_operand (operands[2], DFmode))
    operands[2] = force_reg (DFmode, operands[2]);
}")

(define_insn ""
  [(set (match_operand:DF 0 "register_operand" "=r,x")
	(div:DF (float_extend:DF (match_operand:SF 1 "register_operand" "r,x"))
		(float_extend:DF (match_operand:SF 2 "register_operand" "r,x"))))]
  ""
  "fdiv.dss %0,%1,%2"
  [(set_attr "type" "dpdiv")])

(define_insn ""
  [(set (match_operand:DF 0 "register_operand" "=r,x")
	(div:DF (match_operand:DF 1 "register_operand" "r,x")
		(float_extend:DF (match_operand:SF 2 "register_operand" "r,x"))))]
  ""
  "fdiv.dds %0,%1,%2"
  [(set_attr "type" "dpdiv")])

(define_insn ""
  [(set (match_operand:DF 0 "register_operand" "=r,x")
	(div:DF (float_extend:DF (match_operand:SF 1 "register_operand" "r,x"))
		(match_operand:DF 2 "register_operand" "r,x")))]
  ""
  "fdiv.dsd %0,%1,%2"
  [(set_attr "type" "dpdiv")])

(define_insn "divsf3"
  [(set (match_operand:SF 0 "register_operand" "=r,x")
	(div:SF (match_operand:SF 1 "register_operand" "r,x")
		(match_operand:SF 2 "register_operand" "r,x")))]
  ""
  "fdiv.sss %0,%1,%2"
  [(set_attr "type" "spdiv")])

(define_insn ""
  [(set (match_operand:DF 0 "register_operand" "=r,x")
	(div:DF (match_operand:DF 1 "register_operand" "r,x")
		(match_operand:DF 2 "register_operand" "r,x")))]
  ""
  "fdiv.ddd %0,%1,%2"
  [(set_attr "type" "dpdiv")])

;; - remainder instructions, don't define, since the hardware doesn't have any
;; direct support, and GNU can synthesis them out of div/mul just fine.

;;- load effective address, must come after add, so that we favor using
;;  addu reg,reg,reg  instead of:  lda reg,reg,reg (addu doesn't require
;;  the data unit), and also future 88k chips might not support unscaled
;;  lda instructions.

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r")
	(match_operand:SI 1 "address_operand" "p"))]
  "m88k_gp_threshold > 0 && symbolic_address_p (operands[1])"
  "addu %0,%a1")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r")
	(match_operand:HI 1 "address_operand" "p"))]
  ""
  "lda.h %0,%a1"
  [(set_attr "type" "loada")])

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r")
	(match_operand:SI 1 "address_operand" "p"))]
  ""
  "lda %0,%a1"
  [(set_attr "type" "loada")])

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r")
	(match_operand:DI 1 "address_operand" "p"))]
  ""
  "lda.d %0,%a1"
  [(set_attr "type" "loada")])

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r")
	(match_operand:SF 1 "address_operand" "p"))]
  ""
  "lda %0,%a1"
  [(set_attr "type" "loada")])

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r")
	(match_operand:DF 1 "address_operand" "p"))]
  ""
  "lda.d %0,%a1"
  [(set_attr "type" "loada")])

;;- and instructions (with complement also)
(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r")
	(and:SI (not:SI (match_operand:SI 1 "register_operand" "r"))
		(match_operand:SI 2 "register_operand" "r")))]
  ""
  "and.c %0,%2,%1")

;; If the operation is being performed on a 32-bit constant such that
;; it cannot be done in one insn, do it in two.  We may lose a bit on
;; CSE in pathological cases, but it seems better doing it this way.

(define_expand "andsi3"
  [(set (match_operand:SI 0 "register_operand" "")
	(and:SI (match_operand:SI 1 "arith32_operand" "")
		(match_operand:SI 2 "arith32_operand" "")))]
  ""
  "
{
  if (GET_CODE (operands[2]) == CONST_INT)
    {
      int value = INTVAL (operands[2]);

      if (! (SMALL_INTVAL (value)
	     || (value & 0xffff0000) == 0xffff0000
	     || (value & 0xffff) == 0xffff
	     || (value & 0xffff) == 0
	     || integer_ok_for_set (~value)))
	{
	  emit_insn (gen_andsi3 (operands[0], operands[1],
				 GEN_INT (value | 0xffff)));
	  operands[1] = operands[0];
	  operands[2] = GEN_INT (value | 0xffff0000);
	}
    }
}")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r,r")
	(and:SI (match_operand:SI 1 "arith32_operand" "%r,r")
		(match_operand:SI 2 "arith32_operand" "rIJL,rn")))]
  ""
  "* return output_and (operands);"
  [(set_attr "type" "arith,marith")])

(define_insn ""
  [(set (match_operand:DI 0 "register_operand" "=r")
	(and:DI (not:DI (match_operand:DI 1 "register_operand" "r"))
		(match_operand:DI 2 "register_operand" "r")))]
  ""
  "and.c %d0,%d2,%d1\;and.c %0,%2,%1"
  [(set_attr "type" "marith")])

(define_insn "anddi3"
  [(set (match_operand:DI 0 "register_operand" "=r")
	(and:DI (match_operand:DI 1 "arith64_operand" "%r")
		(match_operand:DI 2 "arith64_operand" "rn")))]
  ""
  "*
{
  rtx xoperands[10];

  xoperands[0] = operand_subword (operands[0], 1, 0, DImode);
  xoperands[1] = operand_subword (operands[1], 1, 0, DImode);
  xoperands[2] = operand_subword (operands[2], 1, 0, DImode);

  output_asm_insn (output_and (xoperands), xoperands);

  operands[0] = operand_subword (operands[0], 0, 0, DImode);
  operands[1] = operand_subword (operands[1], 0, 0, DImode);
  operands[2] = operand_subword (operands[2], 0, 0, DImode);

  return output_and (operands);
}"
  [(set_attr "type" "marith")
   (set_attr "length" "4")]) ; length is 2, 3, or 4.

;;- Bit set (inclusive or) instructions (with complement also)
(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r")
	(ior:SI (not:SI (match_operand:SI 1 "register_operand" "r"))
		(match_operand:SI 2 "register_operand" "r")))]
  ""
  "or.c %0,%2,%1")

(define_expand "iorsi3"
  [(set (match_operand:SI 0 "register_operand" "")
	(ior:SI (match_operand:SI 1 "arith32_operand" "")
		(match_operand:SI 2 "arith32_operand" "")))]
  ""
  "
{
  if (GET_CODE (operands[2]) == CONST_INT)
    {
      int value = INTVAL (operands[2]);

      if (! (SMALL_INTVAL (value)
	     || (value & 0xffff) == 0
	     || integer_ok_for_set (value)))
	{
	  emit_insn (gen_iorsi3 (operands[0], operands[1],
				 GEN_INT (value & 0xffff0000)));
	  operands[1] = operands[0];
	  operands[2] = GEN_INT (value & 0xffff);
	}
    }
}")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r,r,r,r")
	(ior:SI (match_operand:SI 1 "arith32_operand" "%r,r,r,r")
		(match_operand:SI 2 "arith32_operand" "rI,L,M,n")))]
  ""
  "@
   or %0,%1,%2
   or.u %0,%1,%X2
   set %0,%1,%s2
   or.u %0,%1,%X2\;or %0,%0,%x2"
  [(set_attr "type" "arith,arith,bit,marith")])

(define_insn ""
  [(set (match_operand:DI 0 "register_operand" "=r")
	(ior:DI (not:DI (match_operand:DI 1 "register_operand" "r"))
		(match_operand:DI 2 "register_operand" "r")))]
  ""
  "or.c %d0,%d2,%d1\;or.c %0,%2,%1"
  [(set_attr "type" "marith")])

(define_insn "iordi3"
  [(set (match_operand:DI 0 "register_operand" "=r")
	(ior:DI (match_operand:DI 1 "arith64_operand" "%r")
		(match_operand:DI 2 "arith64_operand" "rn")))]
  ""
  "*
{
  rtx xoperands[10];

  xoperands[0] = operand_subword (operands[0], 1, 0, DImode);
  xoperands[1] = operand_subword (operands[1], 1, 0, DImode);
  xoperands[2] = operand_subword (operands[2], 1, 0, DImode);

  output_asm_insn (output_ior (xoperands), xoperands);

  operands[0] = operand_subword (operands[0], 0, 0, DImode);
  operands[1] = operand_subword (operands[1], 0, 0, DImode);
  operands[2] = operand_subword (operands[2], 0, 0, DImode);

  return output_ior (operands);
}"
  [(set_attr "type" "marith")
   (set_attr "length" "4")]) ; length is 2, 3, or 4.

;;- xor instructions (with complement also)
(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r")
	(not:SI (xor:SI (match_operand:SI 1 "register_operand" "%r")
			(match_operand:SI 2 "register_operand" "r"))))]
  ""
  "xor.c %0,%1,%2")

(define_expand "xorsi3"
  [(set (match_operand:SI 0 "register_operand" "")
	(xor:SI (match_operand:SI 1 "arith32_operand" "")
		(match_operand:SI 2 "arith32_operand" "")))]
  ""
  "
{
  if (GET_CODE (operands[2]) == CONST_INT)
    {
      int value = INTVAL (operands[2]);

      if (! (SMALL_INTVAL (value)
	     || (value & 0xffff) == 0))
	{
	  emit_insn (gen_xorsi3 (operands[0], operands[1],
				 GEN_INT (value & 0xffff0000)));
	  operands[1] = operands[0];
	  operands[2] = GEN_INT (value & 0xffff);
	}
    }
}")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r,r,r")
	(xor:SI (match_operand:SI 1 "arith32_operand" "%r,r,r")
		(match_operand:SI 2 "arith32_operand" "rI,L,n")))]
  ""
  "@
   xor %0,%1,%2
   xor.u %0,%1,%X2
   xor.u %0,%1,%X2\;xor %0,%0,%x2"
  [(set_attr "type" "arith,arith,marith")])

(define_insn ""
  [(set (match_operand:DI 0 "register_operand" "=r")
	(not:DI (xor:DI (match_operand:DI 1 "register_operand" "r")
			(match_operand:DI 2 "register_operand" "r"))))]
  ""
  "xor.c %d0,%d1,%d2\;xor.c %0,%1,%2"
  [(set_attr "type" "marith")])

(define_insn "xordi3"
  [(set (match_operand:DI 0 "register_operand" "=r")
	(xor:DI (match_operand:DI 1 "arith64_operand" "%r")
		(match_operand:DI 2 "arith64_operand" "rn")))]
  ""
  "*
{
  rtx xoperands[10];

  xoperands[0] = operand_subword (operands[0], 1, 0, DImode);
  xoperands[1] = operand_subword (operands[1], 1, 0, DImode);
  xoperands[2] = operand_subword (operands[2], 1, 0, DImode);

  output_asm_insn (output_xor (xoperands), xoperands);

  operands[0] = operand_subword (operands[0], 0, 0, DImode);
  operands[1] = operand_subword (operands[1], 0, 0, DImode);
  operands[2] = operand_subword (operands[2], 0, 0, DImode);

  return output_xor (operands);
}"
  [(set_attr "type" "marith")
   (set_attr "length" "4")]) ; length is 2, 3, or 4.

;;- ones complement instructions
(define_insn "one_cmplsi2"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(not:SI (match_operand:SI 1 "register_operand" "r")))]
  ""
  "xor.c %0,%1,%#r0")

(define_insn "one_cmpldi2"
  [(set (match_operand:DI 0 "register_operand" "=r")
	(not:DI (match_operand:DI 1 "register_operand" "r")))]
  ""
  "xor.c %d0,%d1,%#r0\;xor.c %0,%1,%#r0"
  [(set_attr "type" "marith")])

;; Optimized special cases of shifting.
;; Must precede the general case.

;; @@ What about HImode shifted by 8?

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r")
	(ashiftrt:SI (match_operand:SI 1 "memory_operand" "m")
		     (const_int 24)))]
  "! SCALED_ADDRESS_P (XEXP (operands[1], 0))"
  "%V1ld.b\\t %0,%1"
  [(set_attr "type" "load")])

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r")
	(lshiftrt:SI (match_operand:SI 1 "memory_operand" "m")
		     (const_int 24)))]
  "! SCALED_ADDRESS_P (XEXP (operands[1], 0))"
  "%V1ld.bu\\t %0,%1"
  [(set_attr "type" "load")])

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r")
	(ashiftrt:SI (match_operand:SI 1 "memory_operand" "m")
		     (const_int 16)))]
  "! SCALED_ADDRESS_P (XEXP (operands[1], 0))"
  "%V1ld.h\\t %0,%1"
  [(set_attr "type" "load")])

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r")
	(lshiftrt:SI (match_operand:SI 1 "memory_operand" "m")
		     (const_int 16)))]
  "! SCALED_ADDRESS_P (XEXP (operands[1], 0))"
  "%V1ld.hu\\t %0,%1"
  [(set_attr "type" "load")])

;;- arithmetic shift instructions.

;; @@ Do the optimized patterns with -1 get used?  Perhaps operand 1 should
;; be arith32_operand?

;; Use tbnd to support TARGET_TRAP_LARGE_SHIFT.
(define_insn "tbnd"
  [(trap_if (gtu (match_operand:SI 0 "register_operand" "r")
		 (match_operand:SI 1 "arith_operand" "rI"))
	    (const_int 7))]
  ""
  "tbnd %r0,%1"
  [(set_attr "type" "weird")])

;; Just in case the optimizer decides to fold away the test.
(define_insn ""
  [(trap_if (const_int 1) (const_int 7))]
  ""
  "tbnd %#r31,0"
  [(set_attr "type" "weird")])

(define_expand "ashlsi3"
  [(set (match_operand:SI 0 "register_operand" "")
	(ashift:SI (match_operand:SI 1 "register_operand" "")
		   (match_operand:SI 2 "arith32_operand" "")))]
  ""
  "
{
  if (GET_CODE (operands[2]) == CONST_INT)
    {
      if ((unsigned) INTVAL (operands[2]) > 31)
	{
	  if (TARGET_TRAP_LARGE_SHIFT)
	    emit_insn (gen_tbnd (force_reg (SImode, operands[2]),
				 GEN_INT (31)));
	  else
	    emit_move_insn (operands[0], const0_rtx);
	  DONE;
	}
    }

  else if (TARGET_TRAP_LARGE_SHIFT)
    emit_insn (gen_tbnd (operands[2], GEN_INT (31)));

  else if (TARGET_HANDLE_LARGE_SHIFT)
    {
      rtx reg = gen_reg_rtx (SImode);
      emit_insn (gen_cmpsi (operands[2], GEN_INT (31)));
      emit_insn (gen_sleu (reg));
      emit_insn (gen_andsi3 (reg, operands[1], reg));
      operands[1] = reg;
    }
}")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r,r")
	(ashift:SI (match_operand:SI 1 "register_operand" "r,r")
		   (match_operand:SI 2 "arith5_operand" "r,K")))]
  ""
  "@
   mak %0,%1,%2
   mak %0,%1,0<%2>"
  [(set_attr "type" "bit")])

(define_expand "ashrsi3"
  [(set (match_operand:SI 0 "register_operand" "")
	(ashiftrt:SI (match_operand:SI 1 "register_operand" "")
		     (match_operand:SI 2 "arith32_operand" "")))]
  ""
  "
{
  if (GET_CODE (operands[2]) == CONST_INT)
    {
      if ((unsigned) INTVAL (operands[2]) > 31)
	{
	  if (TARGET_TRAP_LARGE_SHIFT)
	    {
	      emit_insn (gen_tbnd (force_reg (SImode, operands[2]),
				   GEN_INT (31)));
	      DONE;
	    }
	  else
	    operands[2] = GEN_INT (31);
	}
    }

  else if (TARGET_TRAP_LARGE_SHIFT)
    emit_insn (gen_tbnd (operands[2], GEN_INT (31)));

  else if (TARGET_HANDLE_LARGE_SHIFT)
    {
      rtx reg = gen_reg_rtx (SImode);
      emit_insn (gen_cmpsi (operands[2], GEN_INT (31)));
      emit_insn (gen_sgtu (reg));
      emit_insn (gen_iorsi3 (reg, operands[2], reg));
      operands[2] = reg;
    }
}")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r,r")
	(ashiftrt:SI (match_operand:SI 1 "register_operand" "r,r")
		     (match_operand:SI 2 "arith5_operand" "r,K")))]
  ""
  "@
   ext %0,%1,%2
   ext %0,%1,0<%2>"
  [(set_attr "type" "bit")])

;;- logical shift instructions.  Logical shift left becomes arithmetic
;; shift left.  

(define_expand "lshrsi3"
  [(set (match_operand:SI 0 "register_operand" "")
	(lshiftrt:SI (match_operand:SI 1 "register_operand" "")
		     (match_operand:SI 2 "arith32_operand" "")))]
  ""
  "
{
  if (GET_CODE (operands[2]) == CONST_INT)
    {
      if ((unsigned) INTVAL (operands[2]) > 31)
	{
	  if (TARGET_TRAP_LARGE_SHIFT)
	    emit_insn (gen_tbnd (force_reg (SImode, operands[2]),
				 GEN_INT (31)));
	  else
	    emit_move_insn (operands[0], const0_rtx);
	  DONE;
	}
    }

  else if (TARGET_TRAP_LARGE_SHIFT)
    emit_insn (gen_tbnd (operands[2], GEN_INT (31)));

  else if (TARGET_HANDLE_LARGE_SHIFT)
    {
      rtx reg = gen_reg_rtx (SImode);
      emit_insn (gen_cmpsi (operands[2], GEN_INT (31)));
      emit_insn (gen_sleu (reg));
      emit_insn (gen_andsi3 (reg, operands[1], reg));
      operands[1] = reg;
    }
}")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r,r")
	(lshiftrt:SI (match_operand:SI 1 "register_operand" "r,r")
		     (match_operand:SI 2 "arith5_operand" "r,K")))]
  ""
  "@
   extu %0,%1,%2
   extu %0,%1,0<%2>"
  [(set_attr "type" "bit")])

;;- rotate instructions

(define_expand "rotlsi3"
  [(set (match_operand:SI 0 "register_operand" "")
	(rotatert:SI (match_operand:SI 1 "register_operand" "")
		     (match_operand:SI 2 "arith32_operand" "")))]
  ""
  "
{
  if (GET_CODE (operands[2]) == CONST_INT
      && (unsigned) INTVAL (operands[2]) >= 32)
    operands[2] = GEN_INT ((32 - INTVAL (operands[2])) % 32);
  else
    {
      rtx op = gen_reg_rtx (SImode);
      emit_insn (gen_negsi2 (op, operands[2]));
      operands[2] = op;
    }
}")

(define_insn "rotrsi3"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(rotatert:SI (match_operand:SI 1 "register_operand" "r")
		     (match_operand:SI 2 "arith_operand" "rI")))]
  ""
  "rot %0,%1,%2"
  [(set_attr "type" "bit")])

;; find first set.

;; The ff1 instruction searches from the most significant bit while ffs
;; searches from the least significant bit.  The bit index and treatment of
;; zero also differ.  This amazing sequence was discovered using the GNU
;; Superoptimizer.

(define_insn "ffssi2"
  [(set (match_operand:SI 0 "register_operand" "=r,&r")
	(ffs:SI (match_operand:SI 1 "register_operand" "0,r")))
   (clobber (reg:CC 0))
   (clobber (match_scratch:SI 2 "=r,X"))]
  ""
  "@
   subu.co %2,%#r0,%1\;and %2,%2,%1\;addu.ci %2,%2,%2\;ff1 %0,%2
   subu.co %0,%#r0,%1\;and %0,%0,%1\;addu.ci %0,%0,%0\;ff1 %0,%0"
  [(set_attr "type" "marith")
   (set_attr "length" "4")])

;; Bit field instructions.

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r")
	(sign_extract:SI (match_operand:SI 1 "register_operand" "r")
			 (const_int 32)
			 (const_int 0)))]
  ""
  "or %0,%#r0,%1")

(define_insn "extv"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(sign_extract:SI (match_operand:SI 1 "register_operand" "r")
			 (match_operand:SI 2 "int5_operand" "")
			 (match_operand:SI 3 "int5_operand" "")))]
  ""
  "*
{
  operands[4] = GEN_INT ((32 - INTVAL (operands[2])) - INTVAL (operands[3]));
  return \"ext %0,%1,%2<%4>\";  /* <(32-%2-%3)> */
}"
  [(set_attr "type" "bit")])

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r")
	(zero_extract:SI (match_operand:SI 1 "register_operand" "r")
			 (const_int 32)
			 (const_int 0)))]
  ""
  "or %0,%#r0,%1")

(define_insn "extzv"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(zero_extract:SI (match_operand:SI 1 "register_operand" "r")
			 (match_operand:SI 2 "int5_operand" "")
			 (match_operand:SI 3 "int5_operand" "")))]
  ""
  "*
{
  operands[4] = GEN_INT ((32 - INTVAL (operands[2])) - INTVAL (operands[3]));
  return \"extu %0,%1,%2<%4>\";  /* <(32-%2-%3)> */
}"
  [(set_attr "type" "bit")])

(define_insn ""
  [(set (zero_extract:SI (match_operand:SI 0 "register_operand" "+r")
			 (match_operand:SI 1 "int5_operand" "")
			 (match_operand:SI 2 "int5_operand" ""))
	(const_int 0))]
  ""
  "*
{
  operands[3] = GEN_INT ((32 - INTVAL (operands[1])) - INTVAL (operands[2]));
  return \"clr %0,%0,%1<%3>\";  /* <(32-%1-%2)> */
}"
  [(set_attr "type" "bit")])

(define_insn ""
  [(set (zero_extract:SI (match_operand:SI 0 "register_operand" "+r")
			 (match_operand:SI 1 "int5_operand" "")
			 (match_operand:SI 2 "int5_operand" ""))
	(const_int -1))]
  ""
  "*
{
  operands[3] = GEN_INT ((32 - INTVAL (operands[1])) - INTVAL (operands[2]));
  return \"set %0,%0,%1<%3>\";  /* <(32-%1-%2)> */
}"
  [(set_attr "type" "bit")])

(define_insn ""
  [(set (zero_extract:SI (match_operand:SI 0 "register_operand" "+r")
			 (match_operand:SI 1 "int5_operand" "")
			 (match_operand:SI 2 "int5_operand" ""))
	(match_operand:SI 3 "int32_operand" "n"))]
  ""
  "*
{
  int value = INTVAL (operands[3]);

  if (INTVAL (operands[1]) < 32)
    value &= (1 << INTVAL (operands[1])) - 1;

  operands[2] = GEN_INT (32 - (INTVAL(operands[1]) + INTVAL(operands[2])));

  value <<= INTVAL (operands[2]);
  operands[3] = GEN_INT (value);

  if (SMALL_INTVAL (value))
    return \"clr %0,%0,%1<%2>\;or %0,%0,%3\";
  else if ((value & 0x0000ffff) == 0)
    return \"clr %0,%0,%1<%2>\;or.u %0,%0,%X3\";
  else
    return \"clr %0,%0,%1<%2>\;or.u %0,%0,%X3\;or %0,%0,%x3\";
}"
  [(set_attr "type" "marith")
   (set_attr "length" "3")]) ; may be 2 or 3.

;; negate insns
(define_insn "negsi2"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(neg:SI (match_operand:SI 1 "arith_operand" "rI")))]
  ""
  "subu %0,%#r0,%1")

(define_insn ""
  [(set (match_operand:SF 0 "register_operand" "=r,x")
	(float_truncate:SF (neg:DF (match_operand:DF 1 "register_operand" "r,x"))))]
  ""
  "@
   fsub.ssd %0,%#r0,%1
   fsub.ssd %0,%#x0,%1"
  [(set_attr "type" "dpadd")])

(define_insn "negdf2"
  [(set (match_operand:DF 0 "register_operand" "=&r,r")
	(neg:DF (match_operand:DF 1 "register_operand" "r,0")))]
  ""
  "@
   xor.u %0,%1,0x8000\;or %d0,%#r0,%d1
   xor.u %0,%0,0x8000"
  [(set_attr "type" "marith,arith")])

(define_insn "negsf2"
  [(set (match_operand:SF 0 "register_operand" "=r")
	(neg:SF (match_operand:SF 1 "register_operand" "r")))]
  ""
  "xor.u %0,%1,0x8000")

;; absolute value insns for floating-point (integer abs can be done using the
;; machine-independent sequence).

(define_insn "absdf2"
  [(set (match_operand:DF 0 "register_operand" "=&r,r")
	(abs:DF (match_operand:DF 1 "register_operand" "r,0")))]
  ""
  "@
   and.u %0,%1,0x7fff\;or %d0,%#r0,%d1
   and.u %0,%0,0x7fff"
  [(set_attr "type" "marith,arith")])

(define_insn "abssf2"
  [(set (match_operand:SF 0 "register_operand" "=r")
	(abs:SF (match_operand:SF 1 "register_operand" "r")))]
  ""
  "and.u %0,%1,0x7fff")

;; Subroutines of "casesi".

;; Operand 0 is index
;; operand 1 is the minimum bound
;; operand 2 is the maximum bound - minimum bound + 1
;; operand 3 is CODE_LABEL for the table;
;; operand 4 is the CODE_LABEL to go to if index out of range.

(define_expand "casesi"
  ;; We don't use these for generating the RTL, but we must describe
  ;; the operands here.
  [(match_operand:SI 0 "general_operand" "")
   (match_operand:SI 1 "immediate_operand" "")
   (match_operand:SI 2 "immediate_operand" "")
   (match_operand 3 "" "")
   (match_operand 4 "" "")]
  ""
  "
{
  register rtx index_diff = gen_reg_rtx (SImode);
  register rtx low = GEN_INT (-INTVAL (operands[1]));
  register rtx label = gen_rtx_LABEL_REF (Pmode, operands[3]);
  register rtx base;

  if (! CASE_VECTOR_INSNS)
    /* These instructions are likely to be scheduled and made loop invariant.
       This decreases the cost of the dispatch at the expense of the default
       case.  */
    base = force_reg (SImode, memory_address_noforce (SImode, label));

  /* Compute the index difference and handle the default case.  */
  emit_insn (gen_addsi3 (index_diff,
			 force_reg (SImode, operands[0]),
			 ADD_INT (low) ? low : force_reg (SImode, low)));
  emit_insn (gen_cmpsi (index_diff, operands[2]));
  /* It's possible to replace this branch with sgtu/iorsi3 and adding a -1
     entry to the table.  However, that doesn't seem to win on the m88110.  */
  emit_jump_insn (gen_bgtu (operands[4]));

  if (CASE_VECTOR_INSNS)
    /* Call the jump that will branch to the appropriate case.  */
    emit_jump_insn (gen_casesi_enter (label, index_diff, operands[3]));
  else
    /* Load the table entry and jump to it.  */
    emit_jump_insn (gen_casesi_jump (gen_reg_rtx (SImode), base, index_diff, operands[3]));

  /* Claim that flow drops into the table so it will be adjacent by not
     emitting a barrier.  */
  DONE;
}")

(define_expand "casesi_jump"
  [(set (match_operand:SI 0 "" "")
	(mem:SI (plus:SI (match_operand:SI 1 "" "")
			 (mult:SI (match_operand:SI 2 "" "")
				  (const_int 4)))))
   (parallel [(set (pc) (match_dup 0))
              (use (label_ref (match_operand 3 "" "")))])]
  ""
  "")

(define_insn ""
  [(set (pc) (match_operand:SI 0 "register_operand" "r"))
   (use (label_ref (match_operand 1 "" "")))]
  ""
  "jmp%. %0"
  [(set_attr "type" "jump")])

;; The bsr.n instruction is directed to the END of the table.  See
;; ASM_OUTPUT_CASE_END.

(define_insn "casesi_enter"
  [(set (pc) (match_operand 0 "" ""))
   (use (match_operand:SI 1 "register_operand" "r"))
   ;; The USE here is so that at least one jump-insn will refer to the label,
   ;; to keep it alive in jump_optimize.
   (use (label_ref (match_operand 2 "" "")))
   (clobber (reg:SI 1))]
  ""
  "*
{
  if (flag_delayed_branch)
    return \"bsr.n %0e\;lda %#r1,%#r1[%1]\";
  m88k_case_index = REGNO (operands[1]);
  return \"bsr %0e\";
}"
  [(set_attr "type" "weird")
   (set_attr "length" "3")]) ; Including the "jmp r1".

;;- jump to subroutine
(define_expand "call"
  [(parallel [(call (match_operand:SI 0 "" "")
		    (match_operand 1 "" ""))
	      (clobber (reg:SI 1))])]
  ""
  "
{
  if (GET_CODE (operands[0]) == MEM
      && ! call_address_operand (XEXP (operands[0], 0), SImode))
    operands[0] = gen_rtx_MEM (GET_MODE (operands[0]),
			       force_reg (Pmode, XEXP (operands[0], 0)));
}")

(define_insn ""
  [(parallel [(call (mem:SI (match_operand:SI 0 "call_address_operand" "rQ"))
		    (match_operand 1 "" ""))
	      (clobber (reg:SI 1))])]
  ""
  "* return output_call (operands, operands[0]);"
  [(set_attr "type" "call")])

(define_expand "call_value"
  [(parallel [(set (match_operand 0 "register_operand" "")
		   (call (match_operand:SI 1 "" "")
			 (match_operand 2 "" "")))
	      (clobber (reg:SI 1))])]
  ""
  "
{
  if (GET_CODE (operands[1]) == MEM
      && ! call_address_operand (XEXP (operands[1], 0), SImode))
    operands[1] = gen_rtx_MEM (GET_MODE (operands[1]),
			       force_reg (Pmode, XEXP (operands[1], 0)));
}")

(define_insn ""
  [(parallel [(set (match_operand 0 "register_operand" "=r")
		   (call (mem:SI
			  (match_operand:SI 1 "call_address_operand" "rQ"))
			 (match_operand 2 "" "")))
	      (clobber (reg:SI 1))])]
  ""
  "* return output_call (operands, operands[1]);"
  [(set_attr "type" "call")])

;; Nop instruction and others

(define_insn "nop"
  [(const_int 0)]
  ""
  "ff0 %#r0,%#r0"
  [(set_attr "type" "bit")])

(define_insn "return"
  [(return)]
  "reload_completed"
  "jmp%. %#r1"
  [(set_attr "type" "jump")])

(define_expand "prologue"
  [(const_int 0)]
  ""
  "m88k_expand_prologue (); DONE;")

(define_expand "epilogue"
  [(return)]
  "! null_prologue ()"
  "m88k_expand_epilogue ();")

(define_insn "blockage"
  [(unspec_volatile [(const_int 0)] 0)]
  ""
  ""
  [(set_attr "length" "0")])

(define_insn "indirect_jump"
  [(set (pc) (match_operand:SI 0 "register_operand" "r"))]
  ""
  "jmp%. %0"
  [(set_attr "type" "jump")])

(define_insn "jump"
  [(set (pc)
	(label_ref (match_operand 0 "" "")))]
  ""
  "br%. %l0"
  [(set_attr "type" "jump")])

;; This insn is used for some loop tests, typically loops reversed when
;; strength reduction is used.  It is actually created when the instruction
;; combination phase combines the special loop test.  Since this insn
;; is both a jump insn and has an output, it must deal with its own
;; reloads, hence the `m' constraints.  The `!' constraints direct reload
;; to not choose the register alternatives in the event a reload is needed.

(define_expand "decrement_and_branch_until_zero"
  [(parallel [(set (pc)
		   (if_then_else
		    (match_operator 0 "relop_no_unsigned"
				    [(match_operand:SI 1 "register_operand" "")
				     (const_int 0)])
		    (label_ref (match_operand 2 "" ""))
		    (pc)))
	      (set (match_dup 1)
		   (plus:SI (match_dup 1)
			    (match_operand:SI 3 "add_operand" "")))
	      (clobber (match_scratch:SI 4 ""))
	      (clobber (match_scratch:SI 5 "=X,X,&r,&r"))])]
  ""
  "")

(define_insn ""
  [(set (pc)
	(if_then_else
	 (match_operator 0 "relop_no_unsigned"
			 [(match_operand:SI 1 "register_operand" "+!r,!r,m,m")
			  (const_int 0)])
	  (label_ref (match_operand 2 "" ""))
	  (pc)))
   (set (match_dup 1)
	(plus:SI (match_dup 1)
		 (match_operand:SI 3 "add_operand" "rI,J,rI,J")))
   (clobber (match_scratch:SI 4 "=X,X,&r,&r"))
   (clobber (match_scratch:SI 5 "=X,X,&r,&r"))]
  "find_reg_note (insn, REG_NONNEG, 0)"
  "@
   bcnd.n %B0,%1,%2\;addu %1,%1,%3
   bcnd.n %B0,%1,%2\;subu %1,%1,%n3
   ld %4,%1\;addu %5,%4,%3\;bcnd.n %B0,%4,%2\;st %5,%1
   ld %4,%1\;subu %5,%4,%n3\;bcnd.n %B0,%4,%2\;st %5,%1"
  [(set_attr "type" "weird")
   (set_attr "length" "2,2,4,4")])

;; Special insn to serve as the last insn of a define_expand.  This insn
;; will generate no code.

(define_expand "dummy"
  [(set (match_operand 0 "" "") (match_dup 0))]
  ""
  "")
