;; This file contains changes made by Data General, December 1989.

;;- Machine description for the Motorola 88000 for GNU C compiler
;;  Copyright (C) 1988,1989 Free Software Foundation, Inc.
;;  Contributed by Michael Tiemann (tiemann@mcc.com)
;;  Additional changes by:
;;	Michael Meissner	(meissner@dg-rtp.dg.com)
;;	Tom Wood		(wood@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 1, 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, 675 Mass Ave, Cambridge, MA 02139, USA.


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

;;- cpp macro #define NOTICE_UPDATE_CC in file tm.h handles condition code
;;- updates for most instructions.

;;- Operand classes for the register allocator:

;; SCCS rev field.  This is a NOP, just to get the SCCS id into the
;; program image.
(define_expand "m88k_sccs_id"
  [(match_operand:SI 0 "" "")]
  ""
  "{ static char sccs_id[] = \"@(#)m88k.md	1.37.3.1 3/16/90 15:41:51\";
     FAIL; }")


;; Virtual argument pointer handling.  We use register 0 internally to
;; indicate usage of the argument pointer.  The following patterns need
;; to occur before the normal patterns, and should be the only ones that
;; deal with the argument pointer in a non-address fashion.  The operand
;; is changed to refer to the frame pointer (r30) or stack pointer (r31)
;; as appropriate.

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r")
	(reg:SI 0))]
  ""
  "*
{
  output_arg_ptr (0);
  operands[1] = gen_rtx (REG, SImode, ARG_POINTER_REGNUM);
  return \"addu %0,%a1\";
}")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r")
	(plus:SI (reg:SI 0)
		 (match_operand:SI 1 "int32_operand" "n")))]
  ""
  "*
{
  output_arg_ptr (INTVAL (operands[1]));
  operands[1] = gen_rtx (REG, SImode, ARG_POINTER_REGNUM);
  return \"addu %0,%a1\";
}")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r")
	(plus:SI (reg:SI 0)
		 (match_operand:SI 1 "register_operand" "r")))]
  ""
  "*
{
  output_arg_ptr (0);
  if ((cc_status.mdep & MDEP_AP_64K) != 0 && m88k_ap_offset == 0)
    {
      operands[2] = ((cc_status.mdep & MDEP_AP_USE_SP) != 0)
		? stack_pointer_rtx
		: frame_pointer_rtx;
      cc_status.mdep &= ~ MDEP_AP_MASK;

      return \"addu %0,%1,%2\";
    }
  else if (REGNO (operands[0]) == REGNO (operands[1]))
    {
      operands[2] = ((cc_status.mdep & MDEP_AP_USE_SP) != 0)
		? stack_pointer_rtx
		: frame_pointer_rtx;
      if ((cc_status.mdep & MDEP_AP_64K) != 0)
	operands[3] = gen_rtx (CONST_INT, VOIDmode, m88k_ap_offset);
      else if ((cc_status.mdep & MDEP_AP_USE_LS) != 0)
	operands[3] = gen_rtx (REG, SImode, LITERAL_SYNTHESIS_REGNUM);
      else if ((cc_status.mdep & MDEP_AP_USE_CC) != 0)
	operands[3] = gen_rtx (REG, SImode, CONDITION_CODE_REGNUM);
      else
	fatal (\"REG += arg pointer\");
      cc_status.mdep &= ~ MDEP_AP_MASK;

      return \"addu %0,%0,%2\\n\\taddu %0,%0,%3\";
    }
  else
    {
      operands[2] = gen_rtx (REG, SImode, ARG_POINTER_REGNUM);
      return \"addu %0,%a2\\n\\taddu %0,%0,%1\";
    }
}")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r")
	(plus:SI (match_operand:SI 1 "register_operand" "r")
		 (reg:SI 0)))]
  ""
  "*
{
  output_arg_ptr (0);
  if ((cc_status.mdep & MDEP_AP_64K) != 0 && m88k_ap_offset == 0)
    {
      operands[2] = ((cc_status.mdep & MDEP_AP_USE_SP) != 0)
		? stack_pointer_rtx
		: frame_pointer_rtx;
      cc_status.mdep &= ~ MDEP_AP_MASK;

      return \"addu %0,%1,%2\";
    }
  else if (REGNO (operands[0]) == REGNO (operands[1]))
    {
      operands[2] = ((cc_status.mdep & MDEP_AP_USE_SP) != 0)
		? stack_pointer_rtx
		: frame_pointer_rtx;
      if ((cc_status.mdep & MDEP_AP_64K) != 0)
	operands[3] = gen_rtx (CONST_INT, VOIDmode, m88k_ap_offset);
      else if ((cc_status.mdep & MDEP_AP_USE_LS) != 0)
	operands[3] = gen_rtx (REG, SImode, LITERAL_SYNTHESIS_REGNUM);
      else if ((cc_status.mdep & MDEP_AP_USE_CC) != 0)
	operands[3] = gen_rtx (REG, SImode, CONDITION_CODE_REGNUM);
      else
	fatal (\"REG += arg pointer\");
      cc_status.mdep &= ~ MDEP_AP_MASK;

      return \"addu %0,%0,%2\\n\\taddu %0,%0,%3\";
    }
  else
    {
      operands[2] = gen_rtx (REG, SImode, ARG_POINTER_REGNUM);
      return \"addu %0,%a2\\n\\taddu %0,%0,%1\";
    }
}")


;; (a << int1) >> int2 optimizations into a single extract.
;; These patterns need to occur before the normal shift patterns

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r")
	(ashiftrt:SI (ashift:SI (match_operand:SI 1 "register_operand" "r")
				(match_operand:SI 2 "int5_operand" "K"))
		     (match_operand:SI 3 "int5_operand" "K")))]
  "INTVAL (operands [2]) <= INTVAL (operands [3])"
  "*
{
  int i2 = INTVAL (operands[2]);
  int i3 = INTVAL (operands[3]);
  operands[4] = gen_rtx (CONST_INT, VOIDmode, i3 - i2);
  operands[5] = (i3 == 32) ? const0_rtx : gen_rtx (CONST_INT, VOIDmode, 32 - i3);
  return \"ext %0,%1,%5<%4>\";
}")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r")
	(ashiftrt:SI (lshift:SI (match_operand:SI 1 "register_operand" "r")
				(match_operand:SI 2 "int5_operand" "K"))
		     (match_operand:SI 3 "int5_operand" "K")))]
  "INTVAL (operands [2]) <= INTVAL (operands [3])"
  "*
{
  int i2 = INTVAL (operands[2]);
  int i3 = INTVAL (operands[3]);
  operands[4] = gen_rtx (CONST_INT, VOIDmode, i3 - i2);
  operands[5] = (i3 == 32) ? const0_rtx : gen_rtx (CONST_INT, VOIDmode, 32 - i3);
  return \"ext %0,%1,%5<%4>\";
}")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r")
	(lshiftrt:SI (ashift:SI (match_operand:SI 1 "register_operand" "r")
				(match_operand:SI 2 "int5_operand" "K"))
		     (match_operand:SI 3 "int5_operand" "K")))]
  "INTVAL (operands [2]) <= INTVAL (operands [3])"
  "*
{
  int i2 = INTVAL (operands[2]);
  int i3 = INTVAL (operands[3]);
  operands[4] = gen_rtx (CONST_INT, VOIDmode, i3 - i2);
  operands[5] = (i3 == 32) ? const0_rtx : gen_rtx (CONST_INT, VOIDmode, 32 - i3);
  return \"extu %0,%1,%5<%4>\";
}")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r")
	(lshiftrt:SI (lshift:SI (match_operand:SI 1 "register_operand" "r")
				(match_operand:SI 2 "int5_operand" "K"))
		     (match_operand:SI 3 "int5_operand" "K")))]
  "INTVAL (operands [2]) <= INTVAL (operands [3])"
  "*
{
  int i2 = INTVAL (operands[2]);
  int i3 = INTVAL (operands[3]);
  operands[4] = gen_rtx (CONST_INT, VOIDmode, i3 - i2);
  operands[5] = (i3 == 32) ? const0_rtx : gen_rtx (CONST_INT, VOIDmode, 32 - i3);
  return \"extu %0,%1,%5<%4>\";
}")


;; Optimizations of 'fast' sign extend (((x & mask) ^ (mask+1)) - (mask+1))
;; into a single ext.  This needs to occur before the normal patterns.
;; Note x & (x-1) == 0 if and only if x is a power of two

;; Putting in special forms to handle sign extending unsigned chars and
;; unsigned shorts directly from memory appears to be too complex for the
;; combiner.  Oh well....

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r")
	(minus:SI (xor:SI (and:SI (match_operand:SI 1 "register_operand" "%r")
				  (match_operand:SI 2 "int32_operand" "n"))
			  (match_operand:SI 3 "int32_operand" "n"))
		  (match_dup 3)))]

  "INTVAL (operands[2]) != 0
   && (INTVAL (operands[2]) & (INTVAL (operands[2])+1)) == 0
   && INTVAL (operands[3]) != 0
   && (INTVAL (operands[3]) & (INTVAL (operands[3])-1)) == 0
   && ((INTVAL (operands[2]) + 1) >> 1) == INTVAL (operands[3])"
  "*
{
  operands[4] = gen_rtx (CONST_INT, VOIDmode, exact_log2 (INTVAL (operands[3])) + 1);
  return \"ext %0,%1,%4<0>\";
}")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r")
	(minus:SI (xor:SI (match_operand:SI 1 "int32_operand" "n")
			  (and:SI (match_operand:SI 2 "register_operand" "%r")
				  (match_operand:SI 3 "int32_operand" "n")))
		  (match_dup 1)))]

  "INTVAL (operands[3]) != 0
   && (INTVAL (operands[3]) & (INTVAL (operands[3])+1)) == 0
   && INTVAL (operands[1]) != 0
   && (INTVAL (operands[1]) & (INTVAL (operands[1])-1)) == 0
   && ((INTVAL (operands[3]) + 1) >> 1) == INTVAL (operands[1])"
  "*
{
  operands[4] = gen_rtx (CONST_INT, VOIDmode, exact_log2 (INTVAL (operands[1])) + 1);
  return \"ext %0,%2,%4<0>\";
}")


;; Compare instructions.
;; This pattern is used for generating an "insn"
;; which does just a compare and sets a (fictitious) condition code.

;; The actual the m88000 insns are compare-and-conditional-jump.
;; The define_insn's below recognize the combinations of
;; compares and jumps, and output each pair as a single assembler insn.

;; This controls RTL generation and register allocation.
(define_insn "cmpsi"
  [(set (cc0)
	(compare (match_operand:SI 0 "arith_operand" "r,rI")
		 (match_operand:SI 1 "arith_operand" "I,r")))]
  ""
  "*
{
  if (! REG_P (operands[0]))
    {
      cc_status.flags |= CC_REVERSED;
      return \"cmp %!,%1,%x0\\t\\t\\t; %i1\";
    }
  else if (!REG_P (operands[1]))
    return \"cmp %!,%0,%x1\\t\\t\\t; %i1\";

  return \"cmp %!,%0,%1\";
}")

;; Patterns for mixed mode floating point, define before the normal patterns

(define_insn ""
  [(set (cc0)
	(compare:DF (match_operand:DF 0 "register_operand" "r")
		    (float_extend:DF (match_operand:SF 1 "register_operand" "r"))))]
  ""
  "fcmp.sds %!,%0,%1")

(define_insn ""
  [(set (cc0)
	(compare:DF (float_extend:DF (match_operand:SF 0 "register_operand" "r"))
		    (match_operand:DF 1 "register_operand" "r")))]
  ""
  "fcmp.ssd %!,%0,%1")

(define_insn "cmpdf"
  [(set (cc0)
	(compare:DF (match_operand:DF 0 "real_or_0_operand" "rG")
		    (match_operand:DF 1 "real_or_0_operand" "rG")))]
  ""
  "*
{
  if (GET_CODE (operands[0]) == CONST_DOUBLE)
    {
      cc_status.flags |= CC_REVERSED;
      return \"fcmp.sds %!,%1,r0\";
    }
  if (GET_CODE (operands[1]) == CONST_DOUBLE)
    return \"fcmp.sds %!,%0,r0\";

  return \"fcmp.sdd %!,%0,%1\";
}")

(define_insn "cmpsf"
  [(set (cc0)
	(compare:SF (match_operand:SF 0 "real_or_0_operand" "rG")
		    (match_operand:SF 1 "real_or_0_operand" "rG")))]
  ""
  "*
{
  if (GET_CODE (operands[0]) == CONST_DOUBLE)
    {
      cc_status.flags |= CC_REVERSED;
      return \"fcmp.sss %!,%1,r0\";
    }
  if (GET_CODE (operands[1]) == CONST_DOUBLE)
    return \"fcmp.sss %!,%0,r0\";

  return \"fcmp.sss %!,%0,%1\";
}")

(define_expand "cmpdi"
  [(set (cc0)
	(compare (match_operand:DI 0 "register_operand" "r")
		 (match_operand:DI 1 "register_operand" "r")))]
  ""
  "
{
  if (!TARGET_LITERAL_SYNTHESIS)
    emit_insn (gen_rtx (CLOBBER, VOIDmode, gen_rtx (REG, SImode, 1)));

  /* fall through, and generate the comparison ops.  */
}")

(define_insn ""
  [(set (cc0)
	(compare (match_operand:DI 0 "register_operand" "r")
		 (match_operand:DI 1 "register_operand" "r")))]
  ""
  "*
{
  int regno;

  if (TARGET_LITERAL_SYNTHESIS)
    {
      if ((cc_status.mdep & MDEP_LS_CHANGE) != 0)
	error (\"output_move_double MDEP_LS_CHANGE bits were set\");
      
      cc_status.mdep &= ~ MDEP_LS_MASK;
      regno = LITERAL_SYNTHESIS_REGNUM;
    }
  else
    regno = 1;			/* use return address register */

  operands[2] = gen_label_rtx ();
  operands[3] = gen_rtx (REG, SImode, regno);

  output_asm_insn (\"cmp %!,%0,%1\\n\\tbb0 eq,%!,%l2\\n\\tcmp %!,%d0,%d1\",
		   operands);

  output_asm_insn (\"extu %3,%!,4<8>\\n\\tclr %!,%!,4<4>\", operands);
  output_asm_insn (\"mak %3,%3,4<4>\\n\\tor %!,%!,%3\", operands);
  output_label (XINT (operands[2], 3));
  return \"\";
}")

;; We have to have this because cse can optimize the previous pattern
;; into this one.

(define_insn "tstsi"
  [(set (cc0)
	(match_operand:SI 0 "register_operand" "r"))]
  ""
  "cmp %!,%0,0")

;; Need this to take a general operand because cse can make
;; a CONST which won't be in a register.
(define_insn ""
  [(set (cc0)
	(match_operand:SI 0 "int16_operand" "I"))]
  ""
  "*
{
  cc_status.flags |= CC_REVERSED;
  return \"cmp %!,r0,%0\";
}")

(define_insn "tstdf"
  [(set (cc0)
	(match_operand:DF 0 "register_operand" "r"))]
  ""
  "fcmp.sds %!,%0,r0")

(define_insn "tstsf"
  [(set (cc0)
	(match_operand:SF 0 "register_operand" "r"))]
  ""
  "fcmp.sss %!,%0,r0")

(define_expand "tstdi"
  [(set (cc0)
	(match_operand:DI 0 "register_operand" "r"))]
  ""
  "
{
  if (!TARGET_LITERAL_SYNTHESIS)
    emit_insn (gen_rtx (CLOBBER, VOIDmode, gen_rtx (REG, SImode, 1)));

  /* fall through, and generate the comparison ops.  */
}")

(define_insn ""
  [(set (cc0)
	(match_operand:DI 0 "register_operand" "r"))]
  ""
  "*
{
  int regno;

  if (TARGET_LITERAL_SYNTHESIS)
    {
      if ((cc_status.mdep & MDEP_LS_CHANGE) != 0)
	error (\"output_move_double MDEP_LS_CHANGE bits were set\");
      
      cc_status.mdep &= ~ MDEP_LS_MASK;
      regno = LITERAL_SYNTHESIS_REGNUM;
    }
  else
    regno = 1;			/* use return address register */

  operands[1] = gen_label_rtx ();
  operands[2] = gen_rtx (REG, SImode, regno);

  output_asm_insn (\"cmp %!,%0,r0\\n\\tbb0 eq,%!,%l1\\n\\tcmp %!,%d0,r0\",
		   operands);

  output_asm_insn (\"extu %2,%!,4<8>\\n\\tclr %!,%!,4<4>\", operands);
  output_asm_insn (\"mak %2,%2,4<4>\\n\\tor %!,%!,%2\", operands);
  output_label (XINT (operands[1], 3));
  return \"\";
}")

;; These control RTL generation for conditional jump insns
;; and match them for register allocation.

(define_insn "beq"
  [(set (pc)
	(if_then_else (eq (cc0)
			  (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "* return output_br_or_call (operands, insn, \"bb1.n eq,%!,%l0\", \"bb1 eq,%!,%l0\"); ")

(define_insn "bne"
  [(set (pc)
	(if_then_else (ne (cc0)
			  (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "* return output_br_or_call (operands, insn, \"bb1.n ne,%!,%l0\", \"bb1 ne,%!,%l0\"); ")

(define_insn "bgt"
  [(set (pc)
	(if_then_else (gt (cc0)
			  (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "* return output_br_or_call (operands, insn, \"bb1.n gt,%!,%l0\", \"bb1 gt,%!,%l0\"); ")

(define_insn "bgtu"
  [(set (pc)
	(if_then_else (gtu (cc0)
			   (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "* return output_br_or_call (operands, insn, \"bb1.n hi,%!,%l0\", \"bb1 hi,%!,%l0\"); ")

(define_insn "blt"
  [(set (pc)
	(if_then_else (lt (cc0)
			  (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "* return output_br_or_call (operands, insn, \"bb1.n lt,%!,%l0\", \"bb1 lt,%!,%l0\"); ")

(define_insn "bltu"
  [(set (pc)
	(if_then_else (ltu (cc0)
			   (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "* return output_br_or_call (operands, insn, \"bb1.n lo,%!,%l0\", \"bb1 lo,%!,%l0\"); ")

(define_insn "bge"
  [(set (pc)
	(if_then_else (ge (cc0)
			  (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "* return output_br_or_call (operands, insn, \"bb1.n ge,%!,%l0\", \"bb1 ge,%!,%l0\"); ")

(define_insn "bgeu"
  [(set (pc)
	(if_then_else (geu (cc0)
			   (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "* return output_br_or_call (operands, insn, \"bb1.n hs,%!,%l0\", \"bb1 hs,%!,%l0\"); ")

(define_insn "ble"
  [(set (pc)
	(if_then_else (le (cc0)
			  (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "* return output_br_or_call (operands, insn, \"bb1.n le,%!,%l0\", \"bb1 le,%!,%l0\"); ")

(define_insn "bleu"
  [(set (pc)
	(if_then_else (leu (cc0)
			   (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "* return output_br_or_call (operands, insn, \"bb1.n ls,%!,%l0\", \"bb1 ls,%!,%l0\"); ")

;; Match inverted jump insns for register allocation.

(define_insn ""
  [(set (pc)
	(if_then_else (match_operator 0 "relop"
				      [(cc0)
				       (const_int 0)])
		      (pc)
		      (label_ref (match_operand 1 "" ""))))]
  ""
  "* return output_br_or_call (operands, insn, \"bb0.n %C0,%!,%l1\", \"bb0 %C0,%!,%l1\"); ")


;; Recognize bcnd instructions for integer values

(define_insn ""
  [(set (pc)
	(if_then_else (eq (match_operand:SI 0 "register_operand" "r")
			  (const_int 0))
		      (match_operand 1 "" "")
		      (match_operand 2 "" "")))]
  ""
  "*
{
  if (GET_CODE (operands[1]) == LABEL_REF)
    return output_br_or_call (operands, insn, \"bcnd.n eq0,%0,%1\", \"bcnd eq0,%0,%1\");
  else
    return output_br_or_call (operands, insn, \"bcnd.n ne0,%0,%2\", \"bcnd ne0,%0,%2\");
}")

(define_insn ""
  [(set (pc)
	(if_then_else (ne (match_operand:SI 0 "register_operand" "r")
			  (const_int 0))
		      (match_operand 1 "" "")
		      (match_operand 2 "" "")))]
  ""
  "*
{
  if (GET_CODE (operands[1]) == LABEL_REF)
    return output_br_or_call (operands, insn, \"bcnd.n ne0,%0,%1\", \"bcnd ne0,%0,%1\");
  else
    return output_br_or_call (operands, insn, \"bcnd.n eq0,%0,%2\", \"bcnd eq0,%0,%2\");
}")

(define_insn ""
  [(set (pc)
	(if_then_else (lt (match_operand:SI 0 "register_operand" "r")
			  (const_int 0))
		      (match_operand 1 "" "")
		      (match_operand 2 "" "")))]
  ""
  "*
{
  if (GET_CODE (operands[1]) == LABEL_REF)
    return output_br_or_call (operands, insn, \"bcnd.n lt0,%0,%1\", \"bcnd lt0,%0,%1\");
  else
    return output_br_or_call (operands, insn, \"bcnd.n ge0,%0,%2\", \"bcnd ge0,%0,%2\");
}")

(define_insn ""
  [(set (pc)
	(if_then_else (le (match_operand:SI 0 "register_operand" "r")
			  (const_int 0))
		      (match_operand 1 "" "")
		      (match_operand 2 "" "")))]
  ""
  "*
{
  if (GET_CODE (operands[1]) == LABEL_REF)
    return output_br_or_call (operands, insn, \"bcnd.n le0,%0,%1\", \"bcnd le0,%0,%1\");
  else
    return output_br_or_call (operands, insn, \"bcnd.n gt0,%0,%2\", \"bcnd gt0,%0,%2\");
}")

(define_insn ""
  [(set (pc)
	(if_then_else (gt (match_operand:SI 0 "register_operand" "r")
			  (const_int 0))
		      (match_operand 1 "" "")
		      (match_operand 2 "" "")))]
  ""
  "*
{
  if (GET_CODE (operands[1]) == LABEL_REF)
    return output_br_or_call (operands, insn, \"bcnd.n gt0,%0,%1\", \"bcnd gt0,%0,%1\");
  else
    return output_br_or_call (operands, insn, \"bcnd.n le0,%0,%2\", \"bcnd le0,%0,%2\");
}")

(define_insn ""
  [(set (pc)
	(if_then_else (ge (match_operand:SI 0 "register_operand" "r")
			  (const_int 0))
		      (match_operand 1 "" "")
		      (match_operand 2 "" "")))]
  ""
  "*
{
  if (GET_CODE (operands[1]) == LABEL_REF)
    return output_br_or_call (operands, insn, \"bcnd.n ge0,%0,%1\", \"bcnd ge0,%0,%1\");
  else
    return output_br_or_call (operands, insn, \"bcnd.n lt0,%0,%2\", \"bcnd lt0,%0,%2\");
}")

;; Recognize bcnd instructions for single precision float values

(define_insn ""
  [(set (pc)
	(if_then_else (eq (float_extend:DF
			   (match_operand:SF 0 "register_operand" "r"))
			  (const_int 0))
		      (match_operand 1 "" "")
		      (match_operand 2 "" "")))]
  ""
  "*
{
  if (GET_CODE (operands[1]) == LABEL_REF)
    return output_br_or_call (operands, insn,
			      \"bcnd.n 0x0a,%0,%1\\t\\t\\t; float == 0.0\",
			      \"bcnd 0x0a,%0,%1\\t\\t\\t; float == 0.0\");
  else
    return output_br_or_call (operands, insn,
			      \"bcnd.n 0x05,%0,%2\\t\\t\\t; float != 0.0\",
			      \"bcnd 0x05,%0,%2\\t\\t\\t; float != 0.0\");
}")

(define_insn ""
  [(set (pc)
	(if_then_else (ne (float_extend:DF
			   (match_operand:SF 0 "register_operand" "r"))
			  (const_int 0))
		      (match_operand 1 "" "")
		      (match_operand 2 "" "")))]
  ""
  "*
{
  if (GET_CODE (operands[1]) == LABEL_REF)
    return output_br_or_call (operands, insn,
			      \"bcnd.n 0x05,%0,%1\\t\\t\\t; float != 0.0\",
			      \"bcnd 0x05,%0,%1\\t\\t\\t; float != 0.0\");
  else
    return output_br_or_call (operands, insn,
			      \"bcnd.n 0x0a,%0,%2\\t\\t\\t; float == 0.0\",
			      \"bcnd 0x0a,%0,%2\\t\\t\\t; float == 0.0\");
}")

(define_insn ""
  [(set (pc)
	(if_then_else (lt (float_extend:DF
			   (match_operand:SF 0 "register_operand" "r"))
			  (const_int 0))
		      (match_operand 1 "" "")
		      (match_operand 2 "" "")))]
  ""
  "*
{
  if (GET_CODE (operands[1]) == LABEL_REF)
    return output_br_or_call (operands, insn,
			      \"bcnd.n 0x04,%0,%1\\t\\t\\t; float < 0.0\",
			      \"bcnd 0x04,%0,%1\\t\\t\\t; float < 0.0\");
  else
    return output_br_or_call (operands, insn,
			      \"bcnd.n 0x0b,%0,%2\\t\\t\\t; float >= 0.0\",
			      \"bcnd 0x0b,%0,%2\\t\\t\\t; float >= 0.0\");
}")

(define_insn ""
  [(set (pc)
	(if_then_else (le (float_extend:DF
			   (match_operand:SF 0 "register_operand" "r"))
			  (const_int 0))
		      (match_operand 1 "" "")
		      (match_operand 2 "" "")))]
  ""
  "*
{
  if (GET_CODE (operands[1]) == LABEL_REF)
    return output_br_or_call (operands, insn,
			      \"bcnd.n 0x0e,%0,%1\\t\\t\\t; float <= 0.0\",
			      \"bcnd 0x0e,%0,%1\\t\\t\\t; float <= 0.0\");
  else
    return output_br_or_call (operands, insn,
			      \"bcnd.n gt0,%0,%2\",
			      \"bcnd gt0,%0,%2\");
}")

(define_insn ""
  [(set (pc)
	(if_then_else (gt (float_extend:DF
			   (match_operand:SF 0 "register_operand" "r"))
			  (const_int 0))
		      (match_operand 1 "" "")
		      (match_operand 2 "" "")))]
  ""
  "*
{
  if (GET_CODE (operands[1]) == LABEL_REF)
    return output_br_or_call (operands, insn,
			      \"bcnd.n gt0,%0,%1\",
			      \"bcnd gt0,%0,%1\");
  else
    return output_br_or_call (operands, insn,
			      \"bcnd.n 0x0e,%0,%2\\t\\t\\t; float <= 0.0\",
			      \"bcnd 0x0e,%0,%2\\t\\t\\t; float <= 0.0\");
}")

(define_insn ""
  [(set (pc)
	(if_then_else (ge (float_extend:DF
			   (match_operand:SF 0 "register_operand" "r"))
			  (const_int 0))
		      (match_operand 1 "" "")
		      (match_operand 2 "" "")))]
  ""
  "*
{
  if (GET_CODE (operands[1]) == LABEL_REF)
    return output_br_or_call (operands, insn,
			      \"bcnd.n 0x0b,%0,%1\\t\\t\\t; float >= 0.0\",
			      \"bcnd 0x0b,%0,%1\\t\\t\\t; float >= 0.0\");
  else
    return output_br_or_call (operands, insn,
			      \"bcnd.n 0x04,%0,%2\\t\\t\\t; float < 0.0\",
			      \"bcnd 0x04,%0,%2\\t\\t\\t; float < 0.0\");
}")

(define_insn ""
  [(set (pc)
	(if_then_else (eq (match_operand:SF 0 "register_operand" "r")
			  (const_int 0))
		      (match_operand 1 "" "")
		      (match_operand 2 "" "")))]
  ""
  "*
{
  if (GET_CODE (operands[1]) == LABEL_REF)
    return output_br_or_call (operands, insn,
			      \"bcnd.n 0x0a,%0,%1\\t\\t\\t; float == 0.0\",
			      \"bcnd 0x0a,%0,%1\\t\\t\\t; float == 0.0\");
  else
    return output_br_or_call (operands, insn,
			      \"bcnd.n 0x05,%0,%2\\t\\t\\t; float != 0.0\",
			      \"bcnd 0x05,%0,%2\\t\\t\\t; float != 0.0\");
}")

(define_insn ""
  [(set (pc)
	(if_then_else (ne (match_operand:SF 0 "register_operand" "r")
			  (const_int 0))
		      (match_operand 1 "" "")
		      (match_operand 2 "" "")))]
  ""
  "*
{
  if (GET_CODE (operands[1]) == LABEL_REF)
    return output_br_or_call (operands, insn,
			      \"bcnd.n 0x05,%0,%1\\t\\t\\t; float != 0.0\",
			      \"bcnd 0x05,%0,%1\\t\\t\\t; float != 0.0\");
  else
    return output_br_or_call (operands, insn,
			      \"bcnd.n 0x0a,%0,%2\\t\\t\\t; float == 0.0\",
			      \"bcnd 0x0a,%0,%2\\t\\t\\t; float == 0.0\");
}")

(define_insn ""
  [(set (pc)
	(if_then_else (lt (match_operand:SF 0 "register_operand" "r")
			  (const_int 0))
		      (match_operand 1 "" "")
		      (match_operand 2 "" "")))]
  ""
  "*
{
  if (GET_CODE (operands[1]) == LABEL_REF)
    return output_br_or_call (operands, insn,
			      \"bcnd.n 0x04,%0,%1\\t\\t\\t; float < 0.0\",
			      \"bcnd 0x04,%0,%1\\t\\t\\t; float < 0.0\");
  else
    return output_br_or_call (operands, insn,
			      \"bcnd.n 0x0b,%0,%2\\t\\t\\t; float >= 0.0\",
			      \"bcnd 0x0b,%0,%2\\t\\t\\t; float >= 0.0\");
}")

(define_insn ""
  [(set (pc)
	(if_then_else (le (match_operand:SF 0 "register_operand" "r")
			  (const_int 0))
		      (match_operand 1 "" "")
		      (match_operand 2 "" "")))]
  ""
  "*
{
  if (GET_CODE (operands[1]) == LABEL_REF)
    return output_br_or_call (operands, insn,
			      \"bcnd.n 0x0e,%0,%1\\t\\t\\t; float <= 0.0\",
			      \"bcnd 0x0e,%0,%1\\t\\t\\t; float <= 0.0\");
  else
    return output_br_or_call (operands, insn,
			      \"bcnd.n gt0,%0,%2\",
			      \"bcnd gt0,%0,%2\");
}")

(define_insn ""
  [(set (pc)
	(if_then_else (gt (match_operand:SF 0 "register_operand" "r")
			  (const_int 0))
		      (match_operand 1 "" "")
		      (match_operand 2 "" "")))]
  ""
  "*
{
  if (GET_CODE (operands[1]) == LABEL_REF)
    return output_br_or_call (operands, insn,
			      \"bcnd.n gt0,%0,%1\",
			      \"bcnd gt0,%0,%1\");
  else
    return output_br_or_call (operands, insn,
			      \"bcnd.n 0x0e,%0,%2\\t\\t\\t; float <= 0.0\",
			      \"bcnd 0x0e,%0,%2\\t\\t\\t; float <= 0.0\");
}")

(define_insn ""
  [(set (pc)
	(if_then_else (ge (match_operand:SF 0 "register_operand" "r")
			  (const_int 0))
		      (match_operand 1 "" "")
		      (match_operand 2 "" "")))]
  ""
  "*
{
  if (GET_CODE (operands[1]) == LABEL_REF)
    return output_br_or_call (operands, insn,
			      \"bcnd.n 0x0b,%0,%1\\t\\t\\t; float >= 0.0\",
			      \"bcnd 0x0b,%0,%1\\t\\t\\t; float >= 0.0\");
  else
    return output_br_or_call (operands, insn,
			      \"bcnd.n 0x04,%0,%2\\t\\t\\t; float < 0.0\",
			      \"bcnd 0x04,%0,%2\\t\\t\\t; float < 0.0\");
}")

;; Recognize bcnd instructions for double precision float values

(define_insn ""
  [(set (pc)
	(if_then_else (eq (match_operand:DF 0 "register_operand" "r")
			  (const_int 0))
		      (match_operand 1 "" "")
		      (match_operand 2 "" "")))]
  ""
  "*
{
  char *template;
  int label_num;

  if (GET_CODE (operands[2]) == LABEL_REF)
    return output_br_or_call (operands, insn,
			      \"bcnd.n 0x05,%0,%2\\t\\t\\t; double != 0.0\\n\\tbcnd ne0,%d0,%2\",
			      \"bcnd 0x05,%0,%2\\t\\t\\t; double != 0.0\\n\\tbcnd ne0,%d0,%2\");

  operands[2] = gen_label_rtx ();
  label_num = XINT (operands[2], 3);
  template = output_br_or_call (operands, insn,
				\"bcnd.n 0x05,%0,%2\\t\\t\\t; double == 0.0\\n\\tbcnd eq0,%d0,%1\",
				\"bcnd 0x05,%0,%2\\t\\t\\t; double == 0.0\\n\\tbcnd eq0,%d0,%1\");

  output_asm_insn (template, operands);
  output_label (label_num);
  return \"\";
}")

(define_insn ""
  [(set (pc)
	(if_then_else (ne (match_operand:DF 0 "register_operand" "r")
			  (const_int 0))
		      (match_operand 1 "" "")
		      (match_operand 2 "" "")))]
  ""
  "*
{
  char *template;
  int label_num;

  if (GET_CODE (operands[1]) == LABEL_REF)
    return output_br_or_call (operands, insn,
			      \"bcnd.n 0x05,%0,%1\\t\\t\\t; double != 0.0\\n\\tbcnd ne0,%d0,%1\",
			      \"bcnd 0x05,%0,%1\\t\\t\\t; double != 0.0\\n\\tbcnd ne0,%d0,%1\");

  operands[1] = gen_label_rtx ();
  label_num = XINT (operands[1], 3);
  template = output_br_or_call (operands, insn,
				\"bcnd.n 0x05,%0,%1\\t\\t\\t; double == 0.0\\n\\tbcnd eq0,%d0,%2\",
				\"bcnd 0x05,%0,%1\\t\\t\\t; double == 0.0\\n\\tbcnd eq0,%d0,%2\");

  output_asm_insn (template, operands);
  output_label (label_num);
  return \"\";
}")

;; Recognize bcnd instructions for double integer values

(define_insn ""
  [(set (pc)
	(if_then_else (eq (zero_extend:DI
			   (match_operand:SI 0 "register_operand" "r"))
			  (const_int 0))
		      (match_operand 1 "" "")
		      (match_operand 2 "" "")))]
  ""
  "*
{
  if (GET_CODE (operands[1]) == LABEL_REF)
    return output_br_or_call (operands, insn,
			      \"bcnd.n eq0,%0,%1\",
			      \"bcnd eq0,%0,%1\");
  else
    return output_br_or_call (operands, insn,
			      \"bcnd.n ne0,%0,%2\",
			      \"bcnd ne0,%0,%2\");
}")

(define_insn ""
  [(set (pc)
	(if_then_else (ne (zero_extend:DI
			   (match_operand:SI 0 "register_operand" "r"))
			  (const_int 0))
		      (match_operand 1 "" "")
		      (match_operand 2 "" "")))]
  ""
  "*
{
  if (GET_CODE (operands[1]) == LABEL_REF)
    return output_br_or_call (operands, insn,
			      \"bcnd.n ne0,%0,%1\",
			      \"bcnd ne0,%0,%1\");
  else
    return output_br_or_call (operands, insn,
			      \"bcnd.n eq0,%0,%2\",
			      \"bcnd eq0,%0,%2\");
}")

(define_insn ""
  [(set (pc)
	(if_then_else (eq (sign_extend:DI
			   (match_operand:SI 0 "register_operand" "r"))
			  (const_int 0))
		      (match_operand 1 "" "")
		      (match_operand 2 "" "")))]
  ""
  "*
{
  if (GET_CODE (operands[1]) == LABEL_REF)
    return output_br_or_call (operands, insn,
			      \"bcnd.n eq0,%0,%1\",
			      \"bcnd eq0,%0,%1\");
  else
    return output_br_or_call (operands, insn,
			      \"bcnd.n ne0,%0,%2\",
			      \"bcnd ne0,%0,%2\");
}")

(define_insn ""
  [(set (pc)
	(if_then_else (ne (sign_extend:DI
			   (match_operand:SI 0 "register_operand" "r"))
			  (const_int 0))
		      (match_operand 1 "" "")
		      (match_operand 2 "" "")))]
  ""
  "*
{
  if (GET_CODE (operands[1]) == LABEL_REF)
    return output_br_or_call (operands, insn,
			      \"bcnd.n ne0,%0,%1\",
			      \"bcnd ne0,%0,%1\");
  else
    return output_br_or_call (operands, insn,
			      \"bcnd.n eq0,%0,%2\",
			      \"bcnd eq0,%0,%2\");
}")

(define_insn ""
  [(set (pc)
	(if_then_else (lt (sign_extend:DI
			   (match_operand:SI 0 "register_operand" "r"))
			  (const_int 0))
		      (match_operand 1 "" "")
		      (match_operand 2 "" "")))]
  ""
  "*
{
  if (GET_CODE (operands[1]) == LABEL_REF)
    return output_br_or_call (operands, insn,
			      \"bcnd.n lt0,%0,%1\",
			      \"bcnd lt0,%0,%1\");
  else
    return output_br_or_call (operands, insn,
			      \"bcnd.n ge0,%0,%2\",
			      \"bcnd ge0,%0,%2\");
}")

(define_insn ""
  [(set (pc)
	(if_then_else (le (sign_extend:DI
			   (match_operand:SI 0 "register_operand" "r"))
			  (const_int 0))
		      (match_operand 1 "" "")
		      (match_operand 2 "" "")))]
  ""
  "*
{
  if (GET_CODE (operands[1]) == LABEL_REF)
    return output_br_or_call (operands, insn,
			      \"bcnd.n le0,%0,%1\",
			      \"bcnd le0,%0,%1\");
  else
    return output_br_or_call (operands, insn,
			      \"bcnd.n gt0,%0,%2\",
			      \"bcnd gt0,%0,%2\");
}")

(define_insn ""
  [(set (pc)
	(if_then_else (gt (sign_extend:DI
			   (match_operand:SI 0 "register_operand" "r"))
			  (const_int 0))
		      (match_operand 1 "" "")
		      (match_operand 2 "" "")))]
  ""
  "*
{
  if (GET_CODE (operands[1]) == LABEL_REF)
    return output_br_or_call (operands, insn,
			      \"bcnd.n gt0,%0,%1\",
			      \"bcnd gt0,%0,%1\");
  else
    return output_br_or_call (operands, insn,
			      \"bcnd.n le0,%0,%2\",
			      \"bcnd le0,%0,%2\");
}")

(define_insn ""
  [(set (pc)
	(if_then_else (ge (sign_extend:DI
			   (match_operand:SI 0 "register_operand" "r"))
			  (const_int 0))
		      (match_operand 1 "" "")
		      (match_operand 2 "" "")))]
  ""
  "*
{
  if (GET_CODE (operands[1]) == LABEL_REF)
    return output_br_or_call (operands, insn,
			      \"bcnd.n ge0,%0,%1\",
			      \"bcnd ge0,%0,%1\");
  else
    return output_br_or_call (operands, insn,
			      \"bcnd.n lt0,%0,%2\",
			      \"bcnd lt0,%0,%2\");
}")

(define_insn ""
  [(set (pc)
	(if_then_else (eq (match_operand:DI 0 "register_operand" "r")
			  (const_int 0))
		      (match_operand 1 "" "")
		      (match_operand 2 "" "")))]
  ""
  "*
{
  CC_STATUS_INIT_NO_MDEP;
  if (GET_CODE (operands[1]) == LABEL_REF)
    return output_br_or_call (operands, insn,
			      \"or %!,%0,%d0\\n\\tbcnd.n eq0,%!,%1\",
			      \"or %!,%0,%d0\\n\\tbcnd eq0,%!,%1\");
  else
    return output_br_or_call (operands, insn,
			      \"or %!,%0,%d0\\n\\tbcnd.n ne0,%!,%2\",
			      \"or %!,%0,%d0\\n\\tbcnd ne0,%!,%2\");
}")

(define_insn ""
  [(set (pc)
	(if_then_else (ne (match_operand:DI 0 "register_operand" "r")
			  (const_int 0))
		      (match_operand 1 "" "")
		      (match_operand 2 "" "")))]
  ""
  "*
{
  CC_STATUS_INIT_NO_MDEP;
  if (GET_CODE (operands[1]) == LABEL_REF)
    return output_br_or_call (operands, insn,
			      \"or %!,%0,%d0\\n\\tbcnd.n ne0,%!,%1\",
			      \"or %!,%0,%d0\\n\\tbcnd ne0,%!,%1\");
  else
    return output_br_or_call (operands, insn,
			      \"or %!,%0,%d0\\n\\tbcnd.n eq0,%!,%2\",
			      \"or %!,%0,%d0\\n\\tbcnd eq0,%!,%2\");
}")

(define_insn ""
  [(set (pc)
	(if_then_else (ge (match_operand:DI 0 "register_operand" "r")
			  (const_int 0))
		      (match_operand 1 "" "")
		      (match_operand 2 "" "")))]
  ""
  "*
{
  if (GET_CODE (operands[1]) == LABEL_REF)
    return output_br_or_call (operands, insn,
			      \"bcnd.n ge0,%0,%1\",
			      \"bcnd ge0,%0,%1\");
  else
    return output_br_or_call (operands, insn,
			      \"bcnd.n lt0,%0,%2\",
			      \"bcnd lt0,%0,%2\");
}")

(define_insn ""
  [(set (pc)
	(if_then_else (gt (match_operand:DI 0 "register_operand" "r")
			  (const_int 0))
		      (match_operand 1 "" "")
		      (match_operand 2 "" "")))]
  ""
  "*
{
  register char *code;

  CC_STATUS_INIT_NO_MDEP;
  if (GET_CODE (operands[1]) == LABEL_REF)
    {
      int label_num;
      operands[2] = gen_label_rtx ();
      label_num = XINT (operands[2], 3);
      code = output_br_or_call (operands, insn,
				\"bcnd.n lt0,%0,%2\\n\\tor %!,%0,%d0\\n\\tbcnd ne0,%!,%1\",
				\"bcnd lt0,%0,%2\\n\\tor %!,%0,%d0\\n\\tbcnd ne0,%!,%1\");
      
      output_asm_insn (code, operands);
      output_label (label_num);
      return \"\";
    }
  else
    return output_br_or_call (operands, insn,
			      \"bcnd.n lt0,%0,%2\\n\\tor %!,%0,%d0\\n\\tbcnd eq0,%!,%2\",
			      \"bcnd lt0,%0,%2\\n\\tor %!,%0,%d0\\n\\tbcnd eq0,%!,%2\");
}")

(define_insn ""
  [(set (pc)
	(if_then_else (le (match_operand:DI 0 "register_operand" "r")
			  (const_int 0))
		      (match_operand 1 "" "")
		      (match_operand 2 "" "")))]
  ""
  "*
{
  register char *code;

  CC_STATUS_INIT_NO_MDEP;
  if (GET_CODE (operands[2]) == LABEL_REF)
    {
      int label_num;
      operands[1] = gen_label_rtx ();
      label_num = XINT (operands[1], 3);
      code = output_br_or_call (operands, insn,
				\"bcnd.n lt0,%0,%1\\n\\tor %!,%0,%d0\\n\\tbcnd ne0,%!,%2\",
				\"bcnd lt0,%0,%1\\n\\tor %!,%0,%d0\\n\\tbcnd ne0,%!,%2\");
      
      output_asm_insn (code, operands);
      output_label (label_num);
      return \"\";
    }
  else
    return output_br_or_call (operands, insn,
			      \"bcnd.n lt0,%0,%1\\n\\tor %!,%0,%d0\\n\\tbcnd eq0,%!,%1\",
			      \"bcnd lt0,%0,%1\\n\\tor %!,%0,%d0\\n\\tbcnd eq0,%!,%1\");
}")

(define_insn ""
  [(set (pc)
	(if_then_else (lt (match_operand:DI 0 "register_operand" "r")
			  (const_int 0))
		      (match_operand 1 "" "")
		      (match_operand 2 "" "")))]
  ""
  "*
{
  if (GET_CODE (operands[1]) == LABEL_REF)
    return output_br_or_call (operands, insn,
			      \"bcnd.n lt0,%0,%1\",
			      \"bcnd lt0,%0,%1\");
  else
    return output_br_or_call (operands, insn,
			      \"bcnd.n ge0,%0,%2\",
			      \"bcnd ge0,%0,%2\");
}")


;; Recognize bb0 and bb1 instructions.

(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" "K"))
	     (const_int 0))
	 (match_operand 2 "" "")
	 (match_operand 3 "" "")))]
  ""
  "*
{
  operands[1] = gen_rtx (CONST_INT, VOIDmode, 31 - INTVAL(operands[1]));
  if (GET_CODE (operands[2]) == LABEL_REF)
    return output_br_or_call (operands, insn,
			      \"bb1.n %1,%0,%2\",
			      \"bb1 %1,%0,%2\");
  else
    return output_br_or_call (operands, insn,
			      \"bb0.n %1,%0,%3\",
			      \"bb0 %1,%0,%3\");
}")

(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" "K"))
	     (const_int 0))
	 (match_operand 2 "" "")
	 (match_operand 3 "" "")))]
  ""
  "*
{
  operands[1] = gen_rtx (CONST_INT, VOIDmode, 31 - INTVAL(operands[1]));
  if (GET_CODE (operands[2]) == LABEL_REF)
    return output_br_or_call (operands, insn,
			      \"bb0.n %1,%0,%2\",
			      \"bb0 %1,%0,%2\");
  else
    return output_br_or_call (operands, insn,
			      \"bb1.n %1,%0,%3\",
			      \"bb1 %1,%0,%3\");
}")

(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" "K"))
	     (const_int 0))
	 (match_operand 2 "" "")
	 (match_operand 3 "" "")))]
  ""
  "*
{
  operands[1] = gen_rtx (CONST_INT, VOIDmode, 31 - INTVAL(operands[1]));
  if (GET_CODE (operands[2]) == LABEL_REF)
    return output_br_or_call (operands, insn,
			      \"bb1.n %1,%0,%2\",
			      \"bb1 %1,%0,%2\");
  else
    return output_br_or_call (operands, insn,
			      \"bb0.n %1,%0,%3\",
			      \"bb0 %1,%0,%3\");
}")

(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" "K"))
	     (const_int 0))
	 (match_operand 2 "" "")
	 (match_operand 3 "" "")))]
  ""
  "*
{
  operands[1] = gen_rtx (CONST_INT, VOIDmode, 31 - INTVAL(operands[1]));
  if (GET_CODE (operands[2]) == LABEL_REF)
    return output_br_or_call (operands, insn,
			      \"bb0.n %1,%0,%2\",
			      \"bb0 %1,%0,%2\");
  else
    return output_br_or_call (operands, insn,
			      \"bb1.n %1,%0,%3\",
			      \"bb1 %1,%0,%3\");
}")


; Note x & (x-1) == 0 if and only if x is a power of two

(define_insn "bb_eq"
  [(set (pc)
	(if_then_else
	 (eq (and:SI (match_operand:SI 0 "register_operand" "%r")
		     (match_operand:SI 1 "bbx_mask_operand" "n"))
	     (const_int 0))
	 (match_operand 2 "" "")
	 (match_operand 3 "" "")))]
  ""
  "*
{
  operands[1]
    = gen_rtx (CONST_INT, VOIDmode, exact_log2 (INTVAL (operands[1])));

  if (GET_CODE (operands[2]) == LABEL_REF)
    return output_br_or_call (operands, insn,
			      \"bb0.n %1,%0,%2\",
			      \"bb0 %1,%0,%2\");
  else
    return output_br_or_call (operands, insn,
			      \"bb1.n %1,%0,%3\",
			      \"bb1 %1,%0,%3\");
}")

(define_insn "bb_ne"
  [(set (pc)
	(if_then_else
	 (ne (and:SI (match_operand:SI 0 "register_operand" "%r")
		     (match_operand:SI 1 "bbx_mask_operand" "n"))
	     (const_int 0))
	 (match_operand 2 "" "")
	 (match_operand 3 "" "")))]
  ""
  "*
{
  operands[1]
    = gen_rtx (CONST_INT, VOIDmode, exact_log2 (INTVAL (operands[1])));

  if (GET_CODE (operands[2]) == LABEL_REF)
    return output_br_or_call (operands, insn,
			      \"bb1.n %1,%0,%2\",
			      \"bb1 %1,%0,%2\");
  else
    return output_br_or_call (operands, insn,
			      \"bb0.n %1,%0,%3\",
			      \"bb0 %1,%0,%3\");
}")


;; Store condition code values into registers

(define_insn "seq"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(eq (cc0)
	    (const_int 0)))]
  ""
  "extu %0,%!,1<eq>")

(define_insn "sne"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(ne (cc0)
	    (const_int 0)))]
  ""
  "extu %0,%!,1<ne>")

(define_insn "slt"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(lt (cc0)
	    (const_int 0)))]
  ""
  "extu %0,%!,1<lt>")

(define_insn "sle"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(le (cc0)
	    (const_int 0)))]
  ""
  "extu %0,%!,1<le>")

(define_insn "sgt"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(gt (cc0)
	    (const_int 0)))]
  ""
  "extu %0,%!,1<gt>")

(define_insn "sge"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(ge (cc0)
	    (const_int 0)))]
  ""
  "extu %0,%!,1<ge>")

(define_insn "sltu"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(ltu (cc0)
	     (const_int 0)))]
  ""
  "extu %0,%!,1<lo>")

(define_insn "sleu"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(leu (cc0)
	     (const_int 0)))]
  ""
  "extu %0,%!,1<ls>")

(define_insn "sgtu"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(gtu (cc0)
	     (const_int 0)))]
  ""
  "extu %0,%!,1<hi>")

(define_insn "sgeu"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(geu (cc0)
	     (const_int 0)))]
  ""
  "extu %0,%!,1<hs>")


;; Move instructions


;; The following are special hooks for doing literal synthesis in the
;; compiler, and to expose the or.u instruction for CSE.

(define_insn "or_hi16"
  [(set (match_operand:SI 0 "register_operand" "r")
	(lshift:SI (zero_extract:SI (match_operand:SI 1 "immediate_operand" "i")
				    (const_int 16)
				    (const_int 0))
		   (const_int 16)))]
  ""
  "or.u %0,r0,hi16(%1)")

(define_insn "or_lo16"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(zero_extract:SI (match_operand:SI 1 "immediate_operand" "i")
			 (const_int 16)
			 (const_int 16)))]
  ""
  "or %0,r0,lo16(%1)")

(define_insn "add_lo16"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(plus:SI (match_operand:SI 1 "register_operand" "r")
		 (zero_extract:SI (match_operand:SI 2 "immediate_operand" "i")
				  (const_int 16)
				  (const_int 16))))]
  ""
  "addu %0,%1,lo16(%2)")

(define_insn ""
  [(set (match_operand:SI 0 "general_operand" "=rm")
	(const_int 0))]
  ""
  "*
{
  if (GET_CODE (operands[0]) == REG || GET_CODE (operands[0]) == SUBREG)
    return \"or %0,r0,r0\";
  else if (GET_CODE (operands[0]) != MEM)
    fatal (\"Tried to store 0 into something that is not a register or memory location\");

  FIX_ADDRESS_IF_NEEDED (operands[0]);
  return \"st r0,%0\";
}")

(define_insn "movsi"
  [(set (match_operand:SI 0 "general_operand" "=g,r")
	(match_operand:SI 1 "general_operand" "r,mi"))]
  ""
  "*
{
  if (REG_P (operands[0]))
    switch (GET_CODE (operands[1]))
      {
	case MEM:
	  FIX_ADDRESS_IF_NEEDED (operands[1]);
	  return \"ld %0,%1\";

	case CONST_INT:
	  return output_load_const_int (SImode, operands, m88k_comment_int);

	case LABEL_REF:
	case SYMBOL_REF:
	case CONST:
	  if (TARGET_STATIC_LIT_SYNTH)
	    return (GET_CODE (operands[1]) == LABEL_REF)
	      ? \"or.u %0,r0,hi16(%1)\\n\\tor %0,%0,lo16(%1)\"
	      : \"addu %0,%#,lo16(%1)\";

	  else if (!TARGET_LITERAL_SYNTHESIS)
	    return (GET_CODE (operands[1]) == MULT ||
		    (GET_CODE (operands[1]) == PLUS &&
		     (GET_CODE (XEXP (operands[1], 0)) == MULT ||
		      GET_CODE (XEXP (operands[1], 1)) == MULT)))
		? \"lda %0,%a1\"
		: \"addu %0,%a1\";

	  else
	    return \"or.u %0,r0,hi16(%1)\\n\\tor %0,%0,lo16(%1)\";

	default:
	  if (REGNO (operands[0]) == REGNO (operands[1]))
	    return \"\";

	  return \"or %0,r0,%1\";
      }
  else
    {
      FIX_ADDRESS_IF_NEEDED (operands[0]);
      return \"st %1,%0\";
    }
}")


(define_insn ""
  [(set (match_operand:HI 0 "general_operand" "=rm")
	(const_int 0))]
  ""
  "*
{
  if (GET_CODE (operands[0]) == REG || GET_CODE (operands[0]) == SUBREG)
    return \"or %0,r0,r0\";
  else if (GET_CODE (operands[0]) != MEM)
    fatal (\"Tried to store 0 into something that is not a register or memory location\");

  FIX_ADDRESS_IF_NEEDED (operands[0]);
  return \"st.h r0,%0\";
}")

(define_insn "movhi"
  [(set (match_operand:HI 0 "general_operand" "=g,r")
	(match_operand:HI 1 "general_operand" "r,mn"))]
  ""
  "*
{
  if (GET_CODE (operands[0]) == REG || GET_CODE (operands[0]) == SUBREG)
    {
      if (GET_CODE (operands[1]) == MEM)
	{
	  FIX_ADDRESS_IF_NEEDED (operands[1]);
	  return \"ld.h %0,%1\";
	}

      if (GET_CODE (operands[1]) == CONST_INT)
	return output_load_const_int (HImode, operands, m88k_comment_int);

      if (REGNO (operands[0]) == REGNO (operands[1]))
 	return \"\";

      return \"or %0,r0,%1\";
    }

  FIX_ADDRESS_IF_NEEDED (operands[0]);
  return \"st.h %1,%0\";
}")


(define_insn ""
  [(set (match_operand:QI 0 "general_operand" "=rm")
	(const_int 0))]
  ""
  "*
{
  if (GET_CODE (operands[0]) == REG || GET_CODE (operands[0]) == SUBREG)
    return \"or %0,r0,r0\";
  else if (GET_CODE (operands[0]) != MEM)
    fatal (\"Tried to store 0 into something that is not a register or memory location\");

  FIX_ADDRESS_IF_NEEDED (operands[0]);
  return \"st.b r0,%0\";
}")

(define_insn "movqi"
  [(set (match_operand:QI 0 "general_operand" "=g,r")
	(match_operand:QI 1 "general_operand" "r,mn"))]
  ""
  "*
{
  if (GET_CODE (operands[0]) == REG || GET_CODE (operands[0]) == SUBREG)
    {
      if (GET_CODE (operands[1]) == MEM)
	{
	  FIX_ADDRESS_IF_NEEDED (operands[1]);
	  return \"ld.b %0,%1\";
	}

      if (GET_CODE (operands[1]) == CONST_INT)
	return output_load_const_int (QImode, operands, m88k_comment_int);

      if (REGNO (operands[0]) == REGNO (operands[1]))
 	return \"\";

      return \"or %0,r0,%1\";
    }

  FIX_ADDRESS_IF_NEEDED (operands[0]);
  return \"st.b %1,%0\";
}")


(define_insn ""
  [(set (match_operand:DI 0 "register_operand" "=r")
	(const_int 0))]
  ""
  "or %0,r0,r0\\n\\tor %d0,r0,r0")

(define_insn "movdi"
  [(set (match_operand:DI 0 "general_operand" "=r,=m")
	(match_operand:DI 1 "general_operand" "rmF,r"))]
  ""
  "*
{
  if (REG_P (operands[0]) && REG_P (operands[1])
      && REGNO (operands[0]) == REGNO (operands[1]))
    return \"\";
  return output_move_double (operands);
}")


(define_insn ""
  [(set (match_operand:DF 0 "register_operand" "=r")
	(match_operand:DF 1 "immediate_operand" "F"))]
   ""
   "* return output_load_const_double (operands);")
  
(define_insn "movdf"
  [(set (match_operand:DF 0 "general_operand" "=r,m")
	(match_operand:DF 1 "general_operand" "rmF,r"))]
  ""
  "*
{
  if (REG_P (operands[0]) && REG_P (operands[1])
      && REGNO (operands[0]) == REGNO (operands[1]))
    return \"\";
  return output_move_double (operands);
}")


(define_insn "movsf"
  [(set (match_operand:SF 0 "general_operand" "=g,r")
	(match_operand:SF 1 "general_operand" "r,mF"))]
  ""
  "*
{
  if (REG_P (operands[0]))
    {
      if (GET_CODE (operands[1]) == MEM)
	{
	  FIX_ADDRESS_IF_NEEDED (operands[1]);
	  return \"ld %0,%1\";
	}

      if (REG_P (operands[1]) && REGNO (operands[0]) == REGNO (operands[1]))
 	return \"\";

      if (GET_CODE (operands[1]) == CONST_DOUBLE)
	return output_load_const_float (operands);

      return \"or %0,r0,%1\";
    }

  FIX_ADDRESS_IF_NEEDED (operands[0]);
  return \"st %1,%0\";
}")


;; Special insn force a value into a register.  This is needed for movstrsi
;; so that the `optimizer' does not convert registers which hold addresses
;; of static items back into the static address, which in this case defeats
;; the purpose of loading the register.  Sigh.....

(define_insn "force_register"
  [(set (match_operand:SI 0 "register_operand" "r")
	(if_then_else
	 (eq (pc) (const_int 0))
	 (match_dup 0)
	 (match_dup 0)))]
  ""
  "")

;; String/block move insn

(define_expand "movstrsi"
  [(parallel [(set (mem:BLK (match_operand:BLK 0 "general_operand" ""))
		   (mem:BLK (match_operand:BLK 1 "general_operand" "")))
	      (use (match_operand:SI 2 "arith32_operand" "rI"))
	      (use (match_operand:SI 3 "int5_operand" "K"))])]
  "TARGET_BLOCK_MOVE"
  "
{
#include \"movstr.h\"
  rtx src;
  rtx dest;
  rtx ptr_src;
  rtx ptr_dest;
  int align = INTVAL (operands[3]);
  rtx size = operands[2];
  int constp = (GET_CODE (size) == CONST_INT);
  int bytes = (constp ? INTVAL(size) : 0);

  if (constp && bytes <= 0)
    {
      warning (\"block move invalid size %d.\", bytes);
      DONE;
    }

  /* Determine machine mode to do move with.  */
  if (align > 4) 
    {
      align = 4;
      operands[3] = gen_rtx (CONST_INT, VOIDmode, align);
    }
  else if (align <= 0 || align == 3)
    fatal (\"block move invalid alignment %d.\", align);

  /* peel off MEM:BLK node to get pointer.  */

  ptr_dest = XEXP (operands[0], 0);
  ptr_src  = XEXP (operands[1], 0);
  dest = copy_to_mode_reg (Pmode, ptr_dest);
  src  = copy_to_mode_reg (Pmode, ptr_src);

  /* See if we can do the move with straight line code.  */
  if (constp && MOVSTR_STRAIGHTP (bytes, align))
    gen_movstrsi_no_loop (ptr_dest, dest, ptr_src, src, bytes, align, 0, 0);

  /* See if we can do the move with a crafted loop.  */
  else if (constp && align == 4)
    gen_movstrsi_loop (ptr_dest, dest, ptr_src, src, bytes, align);

  /* Bail out to memcpy/bcopy.  */
  else
    {
#ifdef TARGET_MEM_FUNCTIONS
      emit_library_call (gen_rtx (SYMBOL_REF, Pmode, \"memcpy\"),
			 0,
			 VOIDmode, 3,
			 dest, Pmode,
			 src, Pmode,
			 size, SImode);
#else
      emit_library_call (gen_rtx (SYMBOL_REF, Pmode, \"bcopy\"),
			 0,
			 VOIDmode, 3,
			 src, Pmode,
			 dest, Pmode,
			 size, SImode);
#endif
    }

  DONE;
}")


;; 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 "register_operand" "=r")
	(match_operand:DF 1 "double_0_operand" "G"))]
   ""
   "or %0,r0,r0\;or %d0,r0,r0")

(define_insn ""
  [(set (match_operand:DF 0 "memory_operand" "=m")
	(match_operand:DF 1 "double_0_operand" "G"))]
   ""
   "*
{
  if (LIT_SYNTH_NEEDED (operands[0]))
    {
      output_ls_address (operands[0], 0);
      return \"st r0,%@,0\;st r0,%@,4\";
    }

  operands[1] = adj_offsettable_operand (operands[0], 4);
  return \"st r0,%0\;st r0,%1\";
}")

;;- truncation instructions
(define_expand "truncsiqi2"
  [(set (match_operand:QI 0 "any_operand" "")
	(truncate:QI (match_operand:SI 1 "register_operand" "r")))]
  ""
  "
{
  if (GET_CODE (operands[1]) != REG && GET_CODE (operands[1]) != SUBREG)
    operands[1] = copy_to_reg (operands[1]);

  /* fall through, generate default rtl */
}")

(define_insn ""
  [(set (match_operand:QI 0 "register_operand" "=r")
	(truncate:QI (match_operand:SI 1 "register_operand" "r")))]
  ""
  "* return (REGNO (operands[0]) == REGNO (operands[1]))
	? \"\"
	: \"or %0,r0,%1\"; ")

(define_insn ""
  [(set (match_operand:QI 0 "memory_operand" "=m")
	(truncate:QI
	 (match_operand:SI 1 "register_operand" "r")))]
  ""
  "*
{
  FIX_ADDRESS_IF_NEEDED (operands[0]);
  return \"st.b %1,%0\";
}")

(define_expand "trunchiqi2"
  [(set (match_operand:QI 0 "any_operand" "")
	(truncate:QI (match_operand:HI 1 "register_operand" "r")))]
  ""
  "
{
  if (GET_CODE (operands[1]) != REG && GET_CODE (operands[1]) != SUBREG)
    operands[1] = copy_to_reg (operands[1]);

  /* fall through, generate default rtl */
}")

(define_insn ""
  [(set (match_operand:QI 0 "register_operand" "=r")
	(truncate:QI (match_operand:HI 1 "register_operand" "r")))]
  ""
  "* return (REGNO (operands[0]) == REGNO (operands[1]))
	? \"\"
	: \"or %0,r0,%1\"; ")

(define_insn ""
  [(set (match_operand:QI 0 "memory_operand" "=m")
	(truncate:QI
	 (match_operand:HI 1 "register_operand" "r")))]
  ""
  "*
{
  FIX_ADDRESS_IF_NEEDED (operands[0]);
  return \"st.b %1,%0\";
}")

(define_expand "truncsihi2"
  [(set (match_operand:HI 0 "any_operand" "")
	(truncate:HI (match_operand:SI 1 "register_operand" "r")))]
  ""
  "
{
  if (GET_CODE (operands[1]) != REG && GET_CODE (operands[1]) != SUBREG)
    operands[1] = copy_to_reg (operands[1]);

  /* fall through, generate default rtl */
}")

(define_insn ""
  [(set (match_operand:HI 0 "register_operand" "=r")
	(truncate:HI (match_operand:SI 1 "register_operand" "r")))]
  ""
  "* return (REGNO (operands[0]) == REGNO (operands[1]))
	? \"\"
	: \"or %0,r0,%1\"; ")

(define_insn ""
  [(set (match_operand:HI 0 "memory_operand" "=m")
	(truncate:HI
	 (match_operand:SI 1 "register_operand" "r")))]
  ""
  "*
{
  FIX_ADDRESS_IF_NEEDED (operands[0]);
  return \"st.h %1,%0\";
}")

(define_expand "truncdiqi2"
  [(set (match_operand:QI 0 "any_operand" "")
	(truncate:QI (match_operand:DI 1 "register_operand" "r")))]
  ""
  "
{
  if (GET_CODE (operands[1]) != REG && GET_CODE (operands[1]) != SUBREG)
    operands[1] = copy_to_reg (operands[1]);

  /* fall through, generate default rtl */
}")

(define_insn ""
  [(set (match_operand:QI 0 "register_operand" "=r")
	(truncate:QI (match_operand:DI 1 "register_operand" "r")))]
  ""
  "* return (REGNO (operands[0]) == REGNO (operands[1]) + 1)
	? \"\"
	: \"or %0,r0,%d1\"; ")

(define_insn ""
  [(set (match_operand:QI 0 "memory_operand" "=m")
	(truncate:QI
	 (match_operand:DI 1 "register_operand" "r")))]
  ""
  "*
{
  FIX_ADDRESS_IF_NEEDED (operands[0]);
  return \"st.b %d1,%0\";
}")

(define_expand "truncdihi2"
  [(set (match_operand:HI 0 "any_operand" "")
	(truncate:HI (match_operand:DI 1 "register_operand" "r")))]
  ""
  "
{
  if (GET_CODE (operands[1]) != REG && GET_CODE (operands[1]) != SUBREG)
    operands[1] = copy_to_reg (operands[1]);

  /* fall through, generate default rtl */
}")

(define_insn ""
  [(set (match_operand:HI 0 "register_operand" "=r")
	(truncate:HI (match_operand:DI 1 "register_operand" "r")))]
  ""
  "* return (REGNO (operands[0]) == REGNO (operands[1]) + 1)
	? \"\"
	: \"or %0,r0,%d1\"; ")

(define_insn ""
  [(set (match_operand:HI 0 "memory_operand" "=m")
	(truncate:HI
	 (match_operand:DI 1 "register_operand" "r")))]
  ""
  "*
{
  FIX_ADDRESS_IF_NEEDED (operands[0]);
  return \"st.h %d1,%0\";
}")

(define_expand "truncdisi2"
  [(set (match_operand:SI 0 "any_operand" "")
	(truncate:SI (match_operand:DI 1 "register_operand" "r")))]
  ""
  "
{
  if (GET_CODE (operands[1]) != REG && GET_CODE (operands[1]) != SUBREG)
    operands[1] = copy_to_reg (operands[1]);

  /* fall through, generate default rtl */
}")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r")
	(truncate:SI (match_operand:DI 1 "register_operand" "r")))]
  ""
  "* return (REGNO (operands[0]) == REGNO (operands[1]) + 1)
	? \"\"
	: \"or %0,r0,%d1\"; ")

(define_insn ""
  [(set (match_operand:SI 0 "memory_operand" "=m")
	(truncate:SI
	 (match_operand:DI 1 "register_operand" "r")))]
  ""
  "*
{
  FIX_ADDRESS_IF_NEEDED (operands[0]);
  return \"st %d1,%0\";
}")


;;- zero extension instructions

;; Note that the one starting from HImode comes before those for QImode
;; so that a constant operand will match HImode, not QImode.

(define_expand "zero_extendhisi2"
  [(set (match_operand:SI 0 "register_operand" "r")
	(zero_extend:SI (match_operand:HI 1 "any_operand" "")))]
  ""
  "
{
  /* fall through and generate default RTL */
}")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r")
	(zero_extend:SI (match_operand:HI 1 "register_operand" "r")))]
  ""
  "mask %0,%1,0xffff");

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r")
	(zero_extend:SI (match_operand:HI 1 "memory_operand" "m")))]
  ""
  "*
{
  FIX_ADDRESS_IF_NEEDED (operands[1]);
  return \"ld.hu %0,%1\";
}")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r")
	(zero_extend:SI (match_operand:HI 1 "int32_operand" "n")))]
  ""
  "*
{
  operands[1] = gen_rtx (CONST_INT, VOIDmode, INTVAL (operands[1]) & 0xffff);
  return output_load_const_int (SImode, operands, m88k_comment_int);
}")

(define_expand "zero_extendqihi2"
  [(set (match_operand:HI 0 "register_operand" "r")
	(zero_extend:HI (match_operand:QI 1 "any_operand" "")))]
  ""
  "
{
  /* fall through and generate default RTL */
}")

(define_insn ""
  [(set (match_operand:HI 0 "register_operand" "=r")
	(zero_extend:HI (match_operand:QI 1 "register_operand" "r")))]
  ""
  "mask %0,%1,0x00ff");

(define_insn ""
  [(set (match_operand:HI 0 "register_operand" "=r")
	(zero_extend:HI (match_operand:QI 1 "memory_operand" "m")))]
  ""
  "*
{
  FIX_ADDRESS_IF_NEEDED (operands[1]);
  return \"ld.bu %0,%1\";
}")

(define_insn ""
  [(set (match_operand:HI 0 "register_operand" "=r")
	(zero_extend:HI (match_operand:QI 1 "int32_operand" "n")))]
  ""
  "*
{
  operands[1] = gen_rtx (CONST_INT, VOIDmode, INTVAL (operands[1]) & 0xff);
  return output_load_const_int (SImode, operands, m88k_comment_int);
}")

(define_expand "zero_extendqisi2"
  [(set (match_operand:SI 0 "register_operand" "r")
	(zero_extend:SI (match_operand:QI 1 "any_operand" "")))]
  ""
  "
{
  /* fall through and generate default RTL */
}")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r")
	(zero_extend:SI (match_operand:QI 1 "register_operand" "r")))]
  ""
  "mask %0,%1,0x00ff");

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r")
	(zero_extend:SI (match_operand:QI 1 "memory_operand" "m")))]
  ""
  "*
{
  FIX_ADDRESS_IF_NEEDED (operands[1]);
  return \"ld.bu %0,%1\";
}")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r")
	(zero_extend:SI (match_operand:QI 1 "int32_operand" "n")))]
  ""
  "*
{
  operands[1] = gen_rtx (CONST_INT, VOIDmode, INTVAL (operands[1]) & 0xff);
  return output_load_const_int (SImode, operands, m88k_comment_int);
}")

(define_expand "zero_extendsidi2"
  [(set (match_operand:DI 0 "register_operand" "r")
	(zero_extend:DI (match_operand:SI 1 "any_operand" "")))]
  ""
  "
{
  /* fall through and generate default RTL */
}")

(define_insn ""
  [(set (match_operand:DI 0 "register_operand" "=r")
	(zero_extend:DI (match_operand:SI 1 "register_operand" "r")))]
  ""
  "* return (REGNO (operands[0]) + 1 == REGNO (operands[1]))
	? \"or %0,r0,r0\"
	: \"or %d0,r0,%1\\n\\tor %0,r0,r0\"; ")

(define_insn ""
  [(set (match_operand:DI 0 "register_operand" "=r")
	(zero_extend:DI (match_operand:SI 1 "memory_operand" "m")))]
  ""
  "*
{
  FIX_ADDRESS_IF_NEEDED (operands[1]);
  return \"ld %d0,%1\\n\\tor %0,r0,r0\";
}")

(define_insn ""
  [(set (match_operand:DI 0 "register_operand" "=r")
	(zero_extend:DI (match_operand:SI 1 "int32_operand" "n")))]
  ""
  "*
{
  rtx xoperands[10];

  xoperands[0] = gen_rtx (REG, SImode, REGNO (operands[0]) + 1);
  xoperands[1] = operands[1];
  output_asm_insn (output_load_const_int (SImode, operands, m88k_comment_int),
		   xoperands);

  return \"or %0,r0,r0\";
}")

(define_expand "zero_extendhidi2"
  [(set (match_operand:DI 0 "register_operand" "r")
	(zero_extend:DI (match_operand:HI 1 "any_operand" "")))]
  ""
  "
{
  /* fall through and generate default RTL */
}")

(define_insn ""
  [(set (match_operand:DI 0 "register_operand" "=r")
	(zero_extend:DI (match_operand:HI 1 "register_operand" "r")))]
  ""
  "mask %d0,%1,0xffff\\n\\tor %0,r0,r0");

(define_insn ""
  [(set (match_operand:DI 0 "register_operand" "=r")
	(zero_extend:DI (match_operand:HI 1 "memory_operand" "m")))]
  ""
  "*
{
  FIX_ADDRESS_IF_NEEDED (operands[1]);
  return \"ld.hu %d0,%1\\n\\tor %0,r0,r0\";
}")

(define_insn ""
  [(set (match_operand:DI 0 "register_operand" "=r")
	(zero_extend:DI (match_operand:HI 1 "int32_operand" "n")))]
  ""
  "*
{
  rtx xoperands[10];

  xoperands[1] = gen_rtx (CONST_INT, VOIDmode,	/* sign extend 16 bits */
			  ((INTVAL (operands[1]) & 0xffff) ^ 0x8000) - 0x8000);

  xoperands[0] = gen_rtx (REG, SImode, REGNO (operands[0]) + 1);
  output_asm_insn (output_load_const_int (SImode, operands, m88k_comment_int),
		   xoperands);

  return \"or %0,r0,r0\";
}")

(define_expand "zero_extendqidi2"
  [(set (match_operand:DI 0 "register_operand" "r")
	(zero_extend:DI (match_operand:QI 1 "any_operand" "")))]
  ""
  "
{
  /* fall through and generate default RTL */
}")

(define_insn ""
  [(set (match_operand:DI 0 "register_operand" "=r")
	(zero_extend:DI (match_operand:QI 1 "register_operand" "r")))]
  ""
  "mask %d0,%1,0x00ff\\n\\tor %0,r0,r0");

(define_insn ""
  [(set (match_operand:DI 0 "register_operand" "=r")
	(zero_extend:DI (match_operand:QI 1 "memory_operand" "m")))]
  ""
  "*
{
  FIX_ADDRESS_IF_NEEDED (operands[1]);
  return \"ld.bu %d0,%1\\n\\tor %0,r0,r0\";
}")

(define_insn ""
  [(set (match_operand:DI 0 "register_operand" "=r")
	(zero_extend:DI (match_operand:QI 1 "int32_operand" "n")))]
  ""
  "*
{
  rtx xoperands[10];

  xoperands[1] = gen_rtx (CONST_INT, VOIDmode,	/* sign extend 8 bits */
			  ((INTVAL (operands[1]) & 0xff) ^ 0x80) - 0x80);

  xoperands[0] = gen_rtx (REG, SImode, REGNO (operands[0]) + 1);
  output_asm_insn (output_load_const_int (SImode, operands, m88k_comment_int),
		   xoperands);

  return \"or %0,r0,r0\";
}")


;;- sign extension instructions

;; Note that the one starting from HImode comes before those for QImode
;; so that a constant operand will match HImode, not QImode.

(define_expand "extendhisi2"
  [(set (match_operand:SI 0 "register_operand" "r")
	(sign_extend:SI (match_operand:HI 1 "any_operand" "")))]
  ""
  "
{
  /* fall through and generate default RTL */
}")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r")
	(sign_extend:SI (match_operand:HI 1 "register_operand" "r")))]
  ""
  "ext %0,%1,16<0>");

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r")
	(sign_extend:SI (match_operand:HI 1 "memory_operand" "m")))]
  ""
  "*
{
  FIX_ADDRESS_IF_NEEDED (operands[1]);
  return \"ld.h %0,%1\";
}")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r")
	(sign_extend:SI (match_operand:HI 1 "int32_operand" "n")))]
  ""
  "*
{
  operands[1] = gen_rtx (CONST_INT, VOIDmode,	/* sign extend 16 bits */
			 ((INTVAL (operands[1]) & 0xffff) ^ 0x8000) - 0x8000);

  return output_load_const_int (SImode, operands, m88k_comment_int);
}")

(define_expand "extendqihi2"
  [(set (match_operand:HI 0 "register_operand" "r")
	(sign_extend:HI (match_operand:QI 1 "any_operand" "")))]
  ""
  "
{
  /* fall through and generate default RTL */
}")

(define_insn ""
  [(set (match_operand:HI 0 "register_operand" "=r")
	(sign_extend:HI (match_operand:QI 1 "register_operand" "r")))]
  ""
  "ext %0,%1,8<0>");

(define_insn ""
  [(set (match_operand:HI 0 "register_operand" "=r")
	(sign_extend:HI (match_operand:QI 1 "memory_operand" "m")))]
  ""
  "*
{
  FIX_ADDRESS_IF_NEEDED (operands[1]);
  return \"ld.b %0,%1\";
}")


(define_insn ""
  [(set (match_operand:HI 0 "register_operand" "=r")
	(sign_extend:HI (match_operand:QI 1 "int32_operand" "n")))]
  ""
  "*
{
  operands[1] = gen_rtx (CONST_INT, VOIDmode,	/* sign extend 8 bits */
			 ((INTVAL (operands[1]) & 0xff) ^ 0x80) - 0x80);

  return output_load_const_int (SImode, operands, m88k_comment_int);
}")

(define_expand "extendqisi2"
  [(set (match_operand:SI 0 "register_operand" "r")
	(sign_extend:SI (match_operand:QI 1 "any_operand" "")))]
  ""
  "
{
  /* fall through and generate default RTL */
}")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r")
	(sign_extend:SI (match_operand:QI 1 "register_operand" "r")))]
  ""
  "ext %0,%1,8<0>");

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r")
	(sign_extend:SI (match_operand:QI 1 "memory_operand" "m")))]
  ""
  "*
{
  FIX_ADDRESS_IF_NEEDED (operands[1]);
  return \"ld.b %0,%1\";
}")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r")
	(sign_extend:SI (match_operand:QI 1 "int32_operand" "n")))]
  ""
  "*
{
  operands[1] = gen_rtx (CONST_INT, VOIDmode,	/* sign extend 8 bits */
			 ((INTVAL (operands[1]) & 0xff) ^ 0x80) - 0x80);

  return output_load_const_int (SImode, operands, m88k_comment_int);
}")

(define_expand "extendsidi2"
  [(set (match_operand:DI 0 "register_operand" "r")
	(sign_extend:DI (match_operand:SI 1 "any_operand" "")))]
  ""
  "
{
  /* fall through and generate default RTL */
}")

(define_insn ""
  [(set (match_operand:DI 0 "register_operand" "=r")
	(sign_extend:DI (match_operand:SI 1 "register_operand" "r")))]
  ""
  "* return (REGNO (operands[0]) + 1 == REGNO (operands[1]))
	? \"ext %0,%d0,1<31>\"
	: \"or %d0,r0,%1\\n\\text %0,%d0,1<31>\"; ")

(define_insn ""
  [(set (match_operand:DI 0 "register_operand" "=r")
	(sign_extend:DI (match_operand:SI 1 "memory_operand" "m")))]
  ""
  "*
{
  FIX_ADDRESS_IF_NEEDED (operands[1]);
  return \"ld %d0,%1\\n\\text %0,%d0,1<31>\";
}")

(define_insn ""
  [(set (match_operand:DI 0 "register_operand" "=r")
	(sign_extend:DI (match_operand:SI 1 "int32_operand" "n")))]
  ""
  "*
{
  rtx xoperands[10];

  xoperands[0] = gen_rtx (REG, SImode, REGNO (operands[0]) + 1);
  xoperands[1] = operands[1];
  output_asm_insn (output_load_const_int (SImode, operands, m88k_comment_int),
		   xoperands);

  return (INTVAL (operands[1]) < 0)
		? \"subu %0,r0,0x0001\\t\\t\\t; -1\"
		: \"or %0,r0,r0\";
}")

(define_expand "extendhidi2"
  [(set (match_operand:DI 0 "register_operand" "r")
	(sign_extend:DI (match_operand:HI 1 "any_operand" "")))]
  ""
  "
{
  /* fall through and generate default RTL */
}")

(define_insn ""
  [(set (match_operand:DI 0 "register_operand" "=r")
	(sign_extend:DI (match_operand:HI 1 "register_operand" "r")))]
  ""
  "ext %d0,%1,16<0>\\n\\text %0,%d0,1<31>");

(define_insn ""
  [(set (match_operand:DI 0 "register_operand" "=r")
	(sign_extend:DI (match_operand:HI 1 "memory_operand" "m")))]
  ""
  "*
{
  FIX_ADDRESS_IF_NEEDED (operands[1]);
  return \"ld.h %d0,%1\\n\\text %0,%d0,1<31>\";
}")

(define_insn ""
  [(set (match_operand:DI 0 "register_operand" "=r")
	(sign_extend:DI (match_operand:HI 1 "int32_operand" "n")))]
  ""
  "*
{
  rtx xoperands[10];

  xoperands[1] = gen_rtx (CONST_INT, VOIDmode,	/* sign extend 8 bits */
			  ((INTVAL (operands[1]) & 0xff) ^ 0x80) - 0x80);

  xoperands[0] = gen_rtx (REG, SImode, REGNO (operands[0]) + 1);
  output_asm_insn (output_load_const_int (SImode, operands, m88k_comment_int),
		   xoperands);

  return (INTVAL (xoperands[1]) < 0)
		? \"subu %0,r0,0x0001\\t\\t\\t; -1\"
		: \"or %0,r0,r0\";
}")

(define_expand "extendqidi2"
  [(set (match_operand:DI 0 "register_operand" "r")
	(sign_extend:DI (match_operand:QI 1 "any_operand" "")))]
  ""
  "
{
  /* fall through and generate default RTL */
}")

(define_insn ""
  [(set (match_operand:DI 0 "register_operand" "=r")
	(sign_extend:DI (match_operand:QI 1 "register_operand" "r")))]
  ""
  "ext %d0,%1,8<0>\\n\\text %0,%d0,1<31>");

(define_insn ""
  [(set (match_operand:DI 0 "register_operand" "=r")
	(sign_extend:DI (match_operand:QI 1 "memory_operand" "m")))]
  ""
  "*
{
  FIX_ADDRESS_IF_NEEDED (operands[1]);
  return \"ld.b %d0,%1\\n\\text %0,%d0,1<31>\";
}")

(define_insn ""
  [(set (match_operand:DI 0 "register_operand" "=r")
	(sign_extend:DI (match_operand:QI 1 "int32_operand" "n")))]
  ""
  "*
{
  rtx xoperands[10];

  xoperands[1] = gen_rtx (CONST_INT, VOIDmode,	/* sign extend 16 bits */
			  ((INTVAL (operands[1]) & 0xffff) ^ 0x8000) - 0x8000);

  xoperands[0] = gen_rtx (REG, SImode, REGNO (operands[0]) + 1);
  output_asm_insn (output_load_const_int (SImode, operands, m88k_comment_int),
		   xoperands);

  return (INTVAL (xoperands[1]) < 0)
		? \"subu %0,r0,0x0001\\t\\t\\t; -1\"
		: \"or %0,r0,r0\";
}")


;; Conversions between float and double.

(define_insn "extendsfdf2"
  [(set (match_operand:DF 0 "register_operand" "=r")
	(float_extend:DF (match_operand:SF 1 "register_operand" "r")))]
  ""
  "fadd.dss %0,r0,%1")

(define_insn "truncdfsf2"
  [(set (match_operand:SF 0 "register_operand" "=r")
	(float_truncate:SF (match_operand:DF 1 "register_operand" "r")))]
  ""
  "fadd.ssd %0,r0,%1")

;; Conversions between floating point and integer

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

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

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

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

;; Conversions between floating point and unsigned

(define_expand "floatunssidf2"
  [(set (match_operand:DF 0 "register_operand" "=r")
	(unsigned_float:DF (match_operand:SI 1 "register_operand" "r")))]
  ""
  "
{
  rtx reg1 = gen_reg_rtx (SImode);	/* 0x8000000 */
  rtx reg2 = gen_reg_rtx (SImode);	/* op[1] + reg1 */
  rtx reg3 = gen_reg_rtx (DFmode);	/* (double) reg3 */
  rtx reg4 = gen_reg_rtx (SFmode);	/* (float)(unsigned)reg1 */
  REAL_VALUE_TYPE offset;

  emit_move_insn (reg1, gen_rtx (CONST_INT, VOIDmode, 0x80000000));
  emit_insn (gen_rtx (SET, VOIDmode, reg2,
		      gen_rtx (PLUS, SImode, operands[1], reg1)));

  emit_insn (gen_floatsidf2 (reg3, reg2));
  offset = REAL_VALUE_LDEXP (1.0, 31);
  emit_move_insn (reg4, immed_real_const_1 (offset, SFmode));
  emit_insn (gen_rtx (SET, VOIDmode, operands[0],
		      gen_rtx (PLUS, DFmode, reg3,
				     gen_rtx (FLOAT_EXTEND, DFmode, reg4))));
  DONE;
}")

(define_expand "floatunssisf2"
  [(set (match_operand:SF 0 "register_operand" "=r")
	(unsigned_float:SF (match_operand:SI 1 "register_operand" "r")))]
  "0 /* NOP for now */"
  "
{
  rtx reg1 = gen_reg_rtx (SImode);	/* 0x8000000 */
  rtx reg2 = gen_reg_rtx (SImode);	/* op[1] + reg1 */
  rtx reg3 = gen_reg_rtx (SFmode);	/* (float) reg3 */
  rtx reg4 = gen_reg_rtx (SFmode);	/* (float)(unsigned)reg1 */
  REAL_VALUE_TYPE offset;

  emit_move_insn (reg1, gen_rtx (CONST_INT, VOIDmode, 0x80000000));
  emit_insn (gen_rtx (SET, VOIDmode, reg2,
		      gen_rtx (PLUS, SImode, operands[1], reg1)));

  emit_insn (gen_floatsisf2 (reg3, reg2));
  offset = REAL_VALUE_LDEXP (1.0, 31);
  emit_move_insn (reg4, immed_real_const_1 (offset, SFmode));
  emit_insn (gen_rtx (SET, VOIDmode, operands[0],
		      gen_rtx (PLUS, SFmode, reg3, reg4)));
  DONE;
}")

(define_expand "fixuns_truncdfsi2"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(unsigned_fix:SI (match_operand:DF 1 "register_operand" "r")))]
  ""
  "
{
  rtx reg1 = gen_reg_rtx (SFmode);
  rtx reg2 = gen_reg_rtx (DFmode);
  rtx reg3 = gen_reg_rtx (SImode);
  rtx label1 = gen_label_rtx ();
  rtx label2 = gen_label_rtx ();
  REAL_VALUE_TYPE offset = REAL_VALUE_LDEXP (1.0, 31);

  emit_move_insn (reg1, immed_real_const_1 (offset, SFmode));
  do_pending_stack_adjust ();
  emit_insn (gen_rtx (SET, VOIDmode, cc0_rtx,
		      gen_rtx (COMPARE, DFmode, operands[1],
			       gen_rtx (FLOAT_EXTEND, DFmode, reg1))));

  emit_jump_insn (gen_bge (label1));

  emit_insn (gen_fix_truncdfsi2 (operands[0], operands[1]));
  emit_jump_insn (gen_rtx (SET, VOIDmode, pc_rtx, gen_rtx (LABEL_REF, VOIDmode, label2)));
  emit_barrier ();

  emit_label (label1);
  emit_move_insn (reg2, gen_rtx (MINUS, DFmode, operands[1],
				 gen_rtx (FLOAT_EXTEND, DFmode, reg1)));

  emit_insn (gen_fix_truncdfsi2 (operands[0], reg2));
  emit_move_insn (reg3, gen_rtx (CONST_INT, VOIDmode, 0x80000000));
  emit_move_insn (operands[0], gen_rtx (PLUS, SImode, operands[0], reg3));

  emit_label (label2);

  /* allow REG_NOTES to be set on last insn (labels don't have enough fields,
     and can't be used for REG_NOTES anyway).  */
  emit_insn (gen_rtx (USE, VOIDmode, stack_pointer_rtx));
  DONE;
}")

(define_expand "fixuns_truncsfsi2"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(unsigned_fix:SI (match_operand:SF 1 "register_operand" "r")))]
  ""
  "
{
  rtx reg1 = gen_reg_rtx (SFmode);
  rtx reg2 = gen_reg_rtx (SFmode);
  rtx reg3 = gen_reg_rtx (SImode);
  rtx label1 = gen_label_rtx ();
  rtx label2 = gen_label_rtx ();
  REAL_VALUE_TYPE offset = REAL_VALUE_LDEXP (1.0, 31);

  emit_move_insn (reg1, immed_real_const_1 (offset, SFmode));
  do_pending_stack_adjust ();
  emit_insn (gen_rtx (SET, VOIDmode, cc0_rtx,
		      gen_rtx (COMPARE, SFmode, operands[1], reg1)));

  emit_jump_insn (gen_bge (label1));

  emit_insn (gen_fix_truncsfsi2 (operands[0], operands[1]));
  emit_jump_insn (gen_rtx (SET, VOIDmode, pc_rtx, gen_rtx (LABEL_REF, VOIDmode, label2)));
  emit_barrier ();

  emit_label (label1);
  emit_move_insn (reg2, gen_rtx (MINUS, SFmode, operands[1], reg1));
  emit_insn (gen_fix_truncsfsi2 (operands[0], reg2));
  emit_move_insn (reg3, gen_rtx (CONST_INT, VOIDmode, 0x80000000));
  emit_move_insn (operands[0], gen_rtx (PLUS, SImode, operands[0], reg3));

  emit_label (label2);

  /* allow REG_NOTES to be set on last insn (labels don't have enough fields,
     and can't be used for REG_NOTES anyway).  */
  emit_insn (gen_rtx (USE, VOIDmode, stack_pointer_rtx));
  DONE;
}")


;;- arithmetic instructions
;;- add instructions
(define_expand "addsi3"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(plus:SI (match_operand:SI 1 "arith32_operand" "rn")
		 (match_operand:SI 2 "arith32_operand" "rn")))]
  ""
  "
{
  enum rtx_code operation = PLUS;
  rtx op1 = operands[1];
  rtx op2 = operands[2];
  rtx op2x;
  int value;
  int need_reg = 0;

  if (GET_CODE (op2) == CONST_INT && GET_CODE (op1) == CONST_INT)
    {
      emit_move_insn (operands[0],
		      gen_rtx (CONST_INT, VOIDmode,
			       INTVAL (op1) + INTVAL (op2)));
      DONE;
    }

  else
    {
     if (GET_CODE (op1) == CONST_INT)
       {
	 op1 = operands[2];	/* reverse operands to get const in immediate*/
	 op2 = operands[1];
       }

     if (GET_CODE (op2) == CONST_INT)
       {
	 value = INTVAL (op2);
	 if (value < 0 && INT_FITS_16_BITS (-value))
	   {
	     op2 = gen_rtx (CONST_INT, VOIDmode, -value);
	     operation = MINUS;
	   }
	 else if (! INT_FITS_16_BITS (value))
	   need_reg = 1;
       }
     else if (! REG_P (op2))
       need_reg = 1;

     if (need_reg)
	{
	  op2x = op2;
	  op2 = gen_reg_rtx (SImode);
	  emit_move_insn (op2, op2x);
	}

     emit_insn (gen_rtx (SET, VOIDmode, operands[0],
			 gen_rtx (operation, SImode, op1, op2)));
     DONE;
   }
}")

;; defering stack pops sometimes creates an addu r31,r31,0
(define_insn ""
 [(set (match_operand:SI 0 "register_operand" "=r")
       (plus:SI (match_operand:SI 1 "register_operand" "r")
		(const_int 0)))]
 ""
 "*
{
  if (REGNO (operands[0]) == REGNO (operands[1]))
    return \"\";

  return \"or %0,r0,%1\";
}")

;; watch for stack allocations
(define_insn ""
  [(set (reg:SI 31)
	(plus:SI (reg:SI 31)
		 (match_operand:SI 0 "register_operand" "r")))]
  ""
  "*
{
  m88k_variable_stack = TRUE;
  return \"addu r31,r31,%0\";
}")

(define_insn ""
  [(set (reg:SI 31)
	(plus:SI (reg:SI 31)
		 (match_operand:SI 0 "signed16_operand" "L")))]
  ""
  "*
{
  m88k_stack_extra -= INTVAL (operands[0]);
  return (INTVAL (operands[0]) >= 0)
		? \"addu r31,r31,%x0\\t\\t\\t; %i0\"
		: \"subu r31,r31,%N0\\t\\t\\t; %n0\";
}")

;; It would be nice if we could tell GNU never to generate a plus if
;; the expression is too big, but too many pieces of the compiler
;; emit naked pluses.  Sigh....

(define_insn ""
  [(set (reg:SI 31)
	(plus:SI (reg:SI 31)
		 (match_operand:SI 0 "int32_operand" "n")))]
  ""
  "*
{
  rtx xoperands[10];

  CC_STATUS_INIT_NO_MDEP;
  m88k_stack_extra -= INTVAL (operands[0]);
  xoperands[1] = operands[0];
  xoperands[0] = gen_rtx (REG, SImode, CONDITION_CODE_REGNUM);

  output_asm_insn (output_load_const_int (SImode, xoperands, m88k_comment_int),
		   xoperands);

  return \"addu r31,r31,%!\";
}")

(define_insn ""
 [(set (match_operand:SI 0 "register_operand" "=r")
       (plus:SI (match_operand:SI 1 "register_operand" "r")
		(match_operand:SI 2 "register_operand" "r")))]
 ""
 "addu %0,%1,%2")

(define_insn ""
 [(set (match_operand:SI 0 "register_operand" "=r")
       (plus:SI (match_operand:SI 1 "register_operand" "%r")
		(match_operand:SI 2 "int32_operand" "n")))]
 ""
 "*
{
  int value;
  rtx xoperands[10];

  if (GET_CODE (operands[2]) != CONST_INT)
    fatal (\"Add pattern where second operand is not an int constant\");

  value = INTVAL (operands[2]);
  if (value < 0 && INT_FITS_16_BITS (-value))
    return \"subu %0,%1,%N2\\t\\t\\t; %n2 (Negated add)\";

  else if (INT_FITS_16_BITS (value))
    return \"addu %0,%1,%x2\\t\\t\\t; %i2\";

  CC_STATUS_INIT_NO_MDEP;
  xoperands[1] = operands[2];
  xoperands[0] = gen_rtx (REG, SImode, CONDITION_CODE_REGNUM);

  output_asm_insn (output_load_const_int (SImode, xoperands, m88k_comment_int),
		   xoperands);

  return \"addu %0,%1,%!\";
}")


;; patterns for mixed mode floating point, define before the normal patterns
;; (needs changes to c-convert.c)
;; 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, wheras the 88000 instruction only rounds once.

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

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

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

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

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

(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"))))]
  ""
  "addu.co %d0,%d1,%2\\n\\taddu.ci %0,%1,r0")

(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")))]
  ""
  "addu.co %d0,%1,%d2\\n\\taddu.ci %0,r0,%2")


(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")))]
  ""
  "addu.co %d0,%d1,%d2\\n\\taddu.ci %0,%1,%2")


;;- subtract instructions
(define_expand "subsi3"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(minus:SI (match_operand:SI 1 "register_operand" "r")
		  (match_operand:SI 2 "arith32_operand" "rn")))]
  ""
  "
{
  enum rtx_code operation = MINUS;
  rtx op2 = operands[2];
  rtx op2x;
  int value;
  int need_reg = 0;

  if (GET_CODE (op2) == CONST_INT && GET_CODE (operands[1]) == CONST_INT)
    {
      emit_move_insn (operands[0],
		      gen_rtx (CONST_INT, VOIDmode,
			       INTVAL (operands[1]) + INTVAL (op2)));
      DONE;
    }

  else
    {
     if (GET_CODE (op2) == CONST_INT)
       {
	 value = INTVAL (op2);
	 if (value < 0 && INT_FITS_16_BITS (-value))
	   {
	     op2 = gen_rtx (CONST_INT, VOIDmode, -value);
	     operation = PLUS;
	   }
	 else if (! INT_FITS_16_BITS (value))
	   need_reg = 1;
       }
     else if (! REG_P (op2))
       need_reg = 1;

     if (need_reg)
	{
	  op2x = op2;
	  op2 = gen_reg_rtx (SImode);
	  emit_move_insn (op2, op2x);
	}

     emit_insn (gen_rtx (SET, VOIDmode, operands[0],
			 gen_rtx (operation, SImode, operands[1], op2)));
     DONE;
   }
}")

;; defering stack pops sometimes creates an subu r31,r31,0
(define_insn ""
 [(set (match_operand:SI 0 "register_operand" "=r")
       (minus:SI (match_operand:SI 1 "register_operand" "r")
		 (const_int 0)))]
 ""
 "*
{
  if (REGNO (operands[0]) == REGNO (operands[1]))
    return \"\";

  return \"or %0,r0,%1\";
}")

;; watch for stack allocations
(define_insn ""
  [(set (reg:SI 31)
	(minus:SI (reg:SI 31)
		  (match_operand:SI 0 "register_operand" "r")))]
  ""
  "*
{
  m88k_variable_stack = TRUE;
  return \"subu r31,r31,%0\";
}")

(define_insn ""
  [(set (reg:SI 31)
	(minus:SI (reg:SI 31)
		  (match_operand:SI 0 "signed16_operand" "L")))]
  ""
  "*
{
  m88k_stack_extra += INTVAL (operands[0]);
  return (INTVAL (operands[0]) >= 0)
		? \"subu r31,r31,%x0\\t\\t\\t; %i0\"
		: \"addu r31,r31,%N0\\t\\t\\t; %n0\";
}")

(define_insn ""
  [(set (reg:SI 31)
	(minus:SI (reg:SI 31)
		  (match_operand:SI 0 "int32_operand" "n")))]
  ""
  "*
{
  rtx xoperands[10];

  CC_STATUS_INIT_NO_MDEP;
  m88k_stack_extra += INTVAL (operands[0]);
  xoperands[1] = operands[0];
  xoperands[0] = gen_rtx (REG, SImode, CONDITION_CODE_REGNUM);

  output_asm_insn (output_load_const_int (SImode, xoperands, m88k_comment_int),
		   xoperands);

  return \"subu r31,r31,%!\";
}")

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

(define_insn ""
 [(set (match_operand:SI 0 "register_operand" "=r")
       (minus:SI (match_operand:SI 1 "register_operand" "r")
		 (match_operand:SI 2 "int32_operand" "n")))]
 ""
 "*
{
  int value;
  rtx xoperands[10];

  if (GET_CODE (operands[2]) != CONST_INT)
    fatal (\"Minus pattern where second operand is not an int constant\");

  value = INTVAL (operands[2]);
  if (value < 0 && INT_FITS_16_BITS (-value))
    return \"addu %0,%1,%N2\\t\\t\\t; %n2 (negated minus)\";

  else if (INT_FITS_16_BITS (value))
    return \"subu %0,%1,%x2\\t\\t\\t; %i2\";

  CC_STATUS_INIT_NO_MDEP;
  xoperands[1] = operands[2];
  xoperands[0] = gen_rtx (REG, SImode, CONDITION_CODE_REGNUM);

  output_asm_insn (output_load_const_int (SImode, xoperands, m88k_comment_int),
		   xoperands);

  return \"subu %0,%1,%!\";
}")

;; patterns for mixed mode floating point, define before the normal patterns
;; 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, wheras the 88000 instruction only rounds once.

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

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

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

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

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

(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"))))]
  ""
  "subu.co %d0,%d1,%2\\n\\tsubu.ci %0,%1,r0")

(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")))]
  ""
  "subu.co %d0,%1,%d2\\n\\tsubu.ci %0,r0,%2")

(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")))]
  ""
  "subu.co %d0,%d1,%d2\\n\\tsubu.ci %0,%1,%2")


;;- multiply instructions
(define_expand "mulsi3"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(mult:SI (match_operand:SI 1 "register_operand" "r")
		 (match_operand:SI 2 "arith32_operand" "rn")))]
  ""
  "
{
  rtx op1 = operands[1];
  rtx op2 = operands[2];
  rtx op2x;

  if (GET_CODE (op2) == CONST_INT && GET_CODE (op1) == CONST_INT)
    {
      emit_move_insn (operands[0],
		      gen_rtx (CONST_INT, VOIDmode,
			       INTVAL (op1) * INTVAL (op2)));
      DONE;
    }

  else
    {
     if (FITS_16_BITS (op1))
       {
	 op1 = operands[2];	/* reverse operands to get const in immediate */
	 op2 = operands[1];
       }

      if (! REG_P (op2))
	{
	  if (GET_CODE (op2) != CONST_INT || (INTVAL (op2) & ~ 0x7ff) != 0)
	    {
	      op2x = op2;
	      op2 = gen_reg_rtx (SImode);
	      emit_move_insn (op2, op2x);
	    }
	}

     emit_insn (gen_rtx (SET, VOIDmode, operands[0],
			 gen_rtx (MULT, SImode, op1, op2)));
     DONE;
   }
}")

(define_insn ""
 [(set (match_operand:SI 0 "register_operand" "=r")
       (mult:SI (match_operand:SI 1 "register_operand" "r")
		(match_operand:SI 2 "register_operand" "r")))]
 ""
 "mul %0,%1,%2")

(define_insn ""
 [(set (match_operand:SI 0 "register_operand" "=r")
       (mult:SI (match_operand:SI 1 "register_operand" "%r")
		(match_operand:SI 2 "int16_operand" "I")))]
 "(INTVAL (operands[2]) & ~ 0x7ff) == 0"
 "mul %0,%1,%x2\\t\\t\\t; %i2")


(define_expand "umulsi3"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(umult:SI (match_operand:SI 1 "register_operand" "r")
		  (match_operand:SI 2 "arith32_operand" "rn")))]
  ""
  "
{
  rtx op1 = operands[1];
  rtx op2 = operands[2];
  rtx op2x;

  if (GET_CODE (op2) == CONST_INT && GET_CODE (op1) == CONST_INT)
    {
      emit_move_insn (operands[0],
		      gen_rtx (CONST_INT, VOIDmode,
			       (unsigned) INTVAL (op1) *
			       (unsigned) INTVAL (op2)));
      DONE;
    }

  else
    {
     if (FITS_16_BITS (op1))
       {
	 op1 = operands[2];	/* reverse operands to get const in immediate */
	 op2 = operands[1];
       }

      if (! REG_P (op2))
	{
	  if (GET_CODE (op2) != CONST_INT || (INTVAL (op2) & ~ 0x7ff) != 0)
	    {
	      op2x = op2;
	      op2 = gen_reg_rtx (SImode);
	      emit_move_insn (op2, op2x);
	    }
	}

     emit_insn (gen_rtx (SET, VOIDmode, operands[0],
			 gen_rtx (UMULT, SImode, op1, op2)));
     DONE;
   }
}")


(define_insn ""
 [(set (match_operand:SI 0 "register_operand" "=r")
       (umult:SI (match_operand:SI 1 "register_operand" "r")
		 (match_operand:SI 2 "register_operand" "r")))]
 ""
 "mul %0,%1,%2")

(define_insn ""
 [(set (match_operand:SI 0 "register_operand" "=r")
       (umult:SI (match_operand:SI 1 "register_operand" "%r")
		 (match_operand:SI 2 "int16_operand" "I")))]
 "(INTVAL (operands[2]) & ~ 0x7ff) == 0"
 "mul %0,%1,%x2\\t\\t\\t; %i2")


;; Optimize multiply by 2.0 into an add (saves 1-3 cycles)
;; Optimize multiply by 1.0 into an add (can't use move because
;; some user in the world might depend on getting a trap).

(define_insn ""
  [(set (match_operand:DF 0 "register_operand" "=r")
	(mult:DF (float_extend:DF (match_operand:SF 1 "register_operand" "r"))
		 (match_operand:DF 2 "real_power_of_2_operand" "H")))]
  "XINT (operands[2], 2) == 0x40000000 ||
   XINT (operands[2], 2) == 0x3ff00000"
  "* return (XINT (operands[2], 2) == 0x40000000)
	? \"fadd.dss %0,%1,%1\"
	: \"fadd.dss %0,r0,%1\"; ")

(define_insn ""
  [(set (match_operand:DF 0 "register_operand" "=r")
	(mult:DF (match_operand:DF 1 "real_power_of_2_operand" "H")
		 (float_extend:DF (match_operand:SF 2 "register_operand" "r"))))]
  "XINT (operands[1], 2) == 0x40000000 ||
   XINT (operands[1], 2) == 0x3ff00000"
  "* return (XINT (operands[1], 2) == 0x40000000)
	? \"fadd.dss %0,%2,%2\"
	: \"fadd.dss %0,r0,%2\"; ")

(define_insn ""
  [(set (match_operand:DF 0 "register_operand" "=r")
	(mult:DF (match_operand:DF 1 "register_operand" "r")
		 (match_operand:DF 2 "real_power_of_2_operand" "H")))]
  "XINT (operands[2], 2) == 0x40000000 ||
   XINT (operands[2], 2) == 0x3ff00000"
  "* return (XINT (operands[2], 2) == 0x40000000)
	? \"fadd.ddd %0,%1,%1\"
	: \"fadd.dsd %0,r0,%1\"; ")

(define_insn ""
  [(set (match_operand:DF 0 "register_operand" "=r")
	(mult:DF (match_operand:DF 1 "real_power_of_2_operand" "H")
		 (match_operand:DF 2 "register_operand" "r")))]
  "XINT (operands[1], 2) == 0x40000000 ||
   XINT (operands[1], 2) == 0x3ff00000"
  "* return (XINT (operands[1], 2) == 0x40000000)
	? \"fadd.ddd %0,%2,%2\"
	: \"fadd.dsd %0,r0,%2\"; ")

(define_insn ""
  [(set (match_operand:SF 0 "register_operand" "=r")
	(mult:SF (match_operand:SF 1 "register_operand" "r")
		 (match_operand:SF 2 "real_power_of_2_operand" "H")))]
  "XINT (operands[2], 2) == 0x40000000 ||
   XINT (operands[2], 2) == 0x3ff00000"
  "* return (XINT (operands[2], 2) == 0x40000000)
	? \"fadd.sss %0,%1,%1\"
	: \"fadd.sss %0,r0,%1\"; ")

(define_insn ""
  [(set (match_operand:SF 0 "register_operand" "=r")
	(mult:SF (match_operand:SF 1 "real_power_of_2_operand" "H")
		 (match_operand:SF 2 "register_operand" "r")))]
  "XINT (operands[1], 2) == 0x40000000 ||
   XINT (operands[1], 2) == 0x3ff00000"
  "* return (XINT (operands[1], 2) == 0x40000000)
	? \"fadd.sss %0,%2,%2\"
	: \"fadd.sss %0,r0,%2\"; ")

;; patterns for mixed mode floating point, define before the normal pattern
;; 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, wheras the 88000 instruction only rounds once.

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

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

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

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

(define_insn "mulsf3"
  [(set (match_operand:SF 0 "register_operand" "=r")
	(mult:SF (match_operand:SF 1 "register_operand" "%r")
		 (match_operand:SF 2 "register_operand" "r")))]
  ""
  "fmul.sss %0,%1,%2")

;;- divide instructions
;; The 88k divide instruction doesn't handle negative numbers, so we
;; have to do it to keep the poor little thing from trapping.  Also,
;; traps by 0 are not reliably reported.  A trap to vector 503
;; asserts divide by zero.

;; Division by 0 trap

(define_insn "div0_trap"
  [(set (pc) (const_int 503))]
  ""
  "tb0 0,r0,503\\t\\t\\t; division by 0 trap")

(define_expand "divsi3"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(div:SI (match_operand:SI 1 "arith32_operand" "rn")
		(match_operand:SI 2 "arith32_operand" "rn")))]
  ""
  "
{
  rtx gen_negsi2 (), gen_tstsi (), gen_jump (), gen_bgt (), gen_bne ();
  rtx gen_div0_trap (), gen_div0_trap (), gen_bge ();
  rtx op1 = operands[1];
  rtx op2 = operands[2];
  rtx join_label;

  if (GET_CODE (op2) == CONST_INT && INTVAL (op2) == 0)
    {
      emit_insn (gen_div0_trap ());
      emit_barrier ();
      DONE;
    }

  if (GET_CODE (op1) == CONST_INT && GET_CODE (op2) == CONST_INT)
    {
      emit_move_insn (operands[0],
		      gen_rtx (CONST_INT, VOIDmode,
			       INTVAL (op1) / INTVAL (op2)));
      DONE;
    }

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

  else if (GET_CODE (op2) == CONST_INT &&
           (INTVAL (operands[2]) & ~ 0x7ff) != 0)
    op2 = force_reg (SImode, op2);		/* E1 silicon fix */

  if (TARGET_USE_DIV)
    {
      emit_move_insn (operands[0], gen_rtx (DIV, SImode, op1, op2));
      if (TARGET_CHECK_ZERO_DIV && GET_CODE (op2) != CONST_INT)
	{
	  rtx label = gen_label_rtx ();
	  emit_insn (gen_tstsi (op2));
	  emit_jump_insn (gen_bne (label));
	  emit_insn (gen_div0_trap ());
	  emit_barrier ();
	  emit_label (label);
	}
      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_rtx (CONST_INT, VOIDmode, -INTVAL (op1));
	}
      op1 = force_reg (SImode, op1);

      emit_insn (gen_negsi2 (neg_op2, op2));
      emit_insn (gen_tstsi (op2));
      emit_jump_insn (gen_bgt (label1));
						/* constant / 0-or-negative */
      emit_move_insn (operands[0], gen_rtx (UDIV, SImode, op1, neg_op2));
      if (!neg)
	emit_insn (gen_negsi2 (operands[0], operands[0]));

      if (TARGET_CHECK_ZERO_DIV)
	{
	  emit_insn (gen_tstsi (op2));
	  emit_jump_insn (gen_bne (join_label));
	  emit_insn (gen_div0_trap ());
	  emit_barrier ();
	}
      else
	{
	  emit_jump_insn (gen_jump (join_label));
	  emit_barrier ();
	}

      emit_label (label1);			/* constant / positive */
      emit_move_insn (operands[0], gen_rtx (UDIV, SImode, op1, op2));
      if (neg)
	emit_insn (gen_negsi2 (operands[0], operands[0]));
    }

  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_rtx (CONST_INT, VOIDmode, -INTVAL (op2));
	}
      else if ((INTVAL (operands[2]) & ~ 0x7ff) != 0)
	op2 = force_reg (SImode, op2);		/* E1 silicon fix */

      emit_insn (gen_negsi2 (neg_op1, op1));
      emit_insn (gen_tstsi (op1));
      emit_jump_insn (gen_bge (label1));
      						/* 0-or-negative / constant */
      emit_move_insn (operands[0], gen_rtx (UDIV, SImode, neg_op1, op2));
      if (!neg)
	emit_insn (gen_negsi2 (operands[0], operands[0]));
      
      emit_jump_insn (gen_jump (join_label));
      emit_barrier ();
      
      emit_label (label1);			/* positive / constant */
      emit_move_insn (operands[0], gen_rtx (UDIV, SImode, op1, op2));
      if (neg)
	emit_insn (gen_negsi2 (operands[0], operands[0]));
    }

  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_tstsi (op2));
      emit_jump_insn (gen_bgt (label1));

      emit_insn (gen_negsi2 (neg_op1, op1));
      emit_insn (gen_tstsi (op1));
      emit_jump_insn (gen_bge (label2));
						/* negative / negative-or-0 */
      emit_move_insn (operands[0], gen_rtx (UDIV, SImode, neg_op1, neg_op2));

      if (TARGET_CHECK_ZERO_DIV)
	{
	  label4 = gen_label_rtx ();
	  emit_insn (gen_tstsi (op2));
	  emit_jump_insn (gen_bne (join_label));
	  emit_label (label4);
	  emit_insn (gen_div0_trap ());
	  emit_barrier ();
	}
      else
	{
	  emit_jump_insn (gen_jump (join_label));
	  emit_barrier ();
	}

      emit_label (label2);			/* pos.-or-0 / neg.-or-0 */
      emit_move_insn (operands[0], gen_rtx (UDIV, SImode, op1, neg_op2));

      if (TARGET_CHECK_ZERO_DIV)
	{
	  emit_insn (gen_tstsi (op2));
	  emit_jump_insn (gen_beq (label4));
	}

      emit_insn (gen_negsi2 (operands[0], operands[0]));
      emit_jump_insn (gen_jump (join_label));
      emit_barrier ();

      emit_label (label1);
      emit_insn (gen_negsi2 (neg_op1, op1));
      emit_insn (gen_tstsi (op1));
      emit_jump_insn (gen_bge (label3));
						/* negative / positive */
      emit_move_insn (operands[0], gen_rtx (UDIV, SImode, neg_op1, op2));
      emit_insn (gen_negsi2 (operands[0], operands[0]));
      emit_jump_insn (gen_jump (join_label));
      emit_barrier ();

      emit_label (label3);			/* positive-or-0 / positive */
      emit_move_insn (operands[0], gen_rtx (UDIV, SImode, op1, op2));
    }

  emit_label (join_label);

  /* allow REG_NOTES to be set on last insn (labels don't have enough fields,
     and can't be used for REG_NOTES anyway).  */
  emit_insn (gen_rtx (USE, VOIDmode, stack_pointer_rtx));
  DONE;
}")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r")
	(div:SI (match_operand:SI 1 "register_operand" "r")
		(match_operand:SI 2 "int16_operand" "I")))]
 "(INTVAL (operands[2]) & ~ 0x7ff) == 0"
  "div %0,%1,%x2\\t\\t\\t; %i2")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r")
	(div:SI (match_operand:SI 1 "register_operand" "r")
		(match_operand:SI 2 "register_operand" "r")))]
  ""
  "div %0,%1,%2");


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

  if (GET_CODE (op2) == CONST_INT && INTVAL (op2) == 0)
    {
      emit_insn (gen_div0_trap ());
      emit_barrier ();
      DONE;
    }
     
  if (GET_CODE (op2) == CONST_INT && GET_CODE (operands[1]) == CONST_INT)
    {
      emit_move_insn (operands[0],
		      gen_rtx (CONST_INT, VOIDmode,
			       ((unsigned) INTVAL (operands[1])) /
			       ((unsigned) INTVAL (operands[2]))));
      DONE;
    }
  else
    {
      if (! REG_P (op2))
	{
	  if (GET_CODE (op2) != CONST_INT || (INTVAL (op2) & ~ 0x7ff) != 0)
	    {
	      op2 = gen_reg_rtx (SImode);
	      emit_move_insn (op2, operands[2]);
	    }
	}

      emit_insn (gen_rtx (SET, VOIDmode, operands[0],
			  gen_rtx (UDIV, SImode, operands[1], op2)));

      if (GET_CODE (op2) != CONST_INT && TARGET_CHECK_ZERO_DIV)
	{
	  rtx label = gen_label_rtx ();
	  emit_insn (gen_tstsi (op2));
	  emit_jump_insn (gen_bne (label));
	  emit_insn (gen_div0_trap ());
	  emit_barrier ();
	  emit_label (label);
	}
      DONE;
    }
}")

(define_insn ""
 [(set (match_operand:SI 0 "register_operand" "=r")
       (udiv:SI (match_operand:SI 1 "register_operand" "r")
	        (match_operand:SI 2 "register_operand" "r")))]
 ""
 "divu %0,%1,%2")

(define_insn ""
 [(set (match_operand:SI 0 "register_operand" "=r")
       (udiv:SI (match_operand:SI 1 "register_operand" "r")
	        (match_operand:SI 2 "int16_operand" "I")))]
 "(INTVAL (operands[2]) & ~ 0x7ff) == 0"
 "divu %0,%1,%x2\\t\\t\\t; %i2")


;; patterns to handle x/c where c is a power of two, by converting it
;; to x * (1/c)

(define_insn ""
  [(set (match_operand:DF 0 "register_operand" "=r")
	(div:DF (float_extend:DF (match_operand:SF 1 "register_operand" "r"))
		(match_operand:DF 2 "real_power_of_2_operand" "H")))]
  ""
  "* output_divide_by_power_of_2 (operands); ")

(define_insn ""
  [(set (match_operand:DF 0 "register_operand" "=r")
	(div:DF (match_operand:DF 1 "register_operand" "r")
		(float_extend:DF (match_operand:SF 2 "real_power_of_2_operand" "H"))))]
  ""
  "* output_divide_by_power_of_2 (operands); ")

(define_insn ""
  [(set (match_operand:DF 0 "register_operand" "=r")
	(div:DF (float_extend:DF (match_operand:SF 1 "register_operand" "r"))
		(float_extend:DF (match_operand:SF 2 "real_power_of_2_operand" "H"))))]
  ""
  "* output_divide_by_power_of_2 (operands); ")

(define_insn ""
  [(set (match_operand:DF 0 "register_operand" "=r")
	(float_extend:DF (div:SF (match_operand:SF 1 "register_operand" "r")
				 (match_operand:SF 2 "real_power_of_2_operand" "H"))))]
  ""
  "* output_divide_by_power_of_2 (operands); ")

(define_insn ""
  [(set (match_operand:DF 0 "register_operand" "=r")
	(float_extend:DF (div:SF (match_operand:SF 1 "real_power_of_2_operand" "H")
				 (match_operand:SF 2 "register_operand" "r"))))]
  ""
  "* output_divide_by_power_of_2 (operands); ")

(define_insn ""
  [(set (match_operand:DF 0 "register_operand" "=r")
	(div:DF (match_operand:DF 1 "register_operand" "r")
		(match_operand:DF 2 "real_power_of_2_operand" "H")))]
  ""
  "* output_divide_by_power_of_2 (operands); ")

(define_insn ""
  [(set (match_operand:SF 0 "register_operand" "=r")
	(div:SF (match_operand:SF 1 "register_operand" "r")
		(match_operand:SF 2 "real_power_of_2_operand" "H")))]
  ""
  "* output_divide_by_power_of_2 (operands); ")

;; patterns for mixed mode floating point, define before the normal pattern
;; 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, wheras the 88000 instruction only rounds once.

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

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

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

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

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

;; - 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:QI 1 "simple_address_operand" "p"))]
  ""
  "*
{
  FIX_ADDRESS_IF_NEEDED (operands[1]);
  return (GET_CODE (operands[1]) == MULT ||
	  (GET_CODE (operands[1]) == PLUS &&
	   (GET_CODE (XEXP (operands[1], 0)) == MULT ||
	    GET_CODE (XEXP (operands[1], 1)) == MULT)))
	? \"lda.b %0,%a1\"
	: \"addu %0,%a1\";
}")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r")
	(match_operand:HI 1 "simple_address_operand" "p"))]
  ""
  "*
{
  FIX_ADDRESS_IF_NEEDED (operands[1]);
  return (GET_CODE (operands[1]) == MULT ||
	  (GET_CODE (operands[1]) == PLUS &&
	   (GET_CODE (XEXP (operands[1], 0)) == MULT ||
	    GET_CODE (XEXP (operands[1], 1)) == MULT)))
	? \"lda.h %0,%a1\"
	: \"addu %0,%a1\";
}")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r")
	(match_operand:SI 1 "simple_address_operand" "p"))]
  ""
  "*
{
  FIX_ADDRESS_IF_NEEDED (operands[1]);
  return (GET_CODE (operands[1]) == MULT ||
	  (GET_CODE (operands[1]) == PLUS &&
	   (GET_CODE (XEXP (operands[1], 0)) == MULT ||
	    GET_CODE (XEXP (operands[1], 1)) == MULT)))
	? \"lda %0,%a1\"
	: \"addu %0,%a1\";
}")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r")
	(match_operand:DI 1 "simple_address_operand" "p"))]
  ""
  "*
{
  FIX_ADDRESS_IF_NEEDED (operands[1]);
  return (GET_CODE (operands[1]) == MULT ||
	  (GET_CODE (operands[1]) == PLUS &&
	   (GET_CODE (XEXP (operands[1], 0)) == MULT ||
	    GET_CODE (XEXP (operands[1], 1)) == MULT)))
	? \"lda.d %0,%a1\"
	: \"addu %0,%a1\";
}")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r")
	(match_operand:SF 1 "simple_address_operand" "p"))]
  ""
  "*
{
  FIX_ADDRESS_IF_NEEDED (operands[1]);
  return (GET_CODE (operands[1]) == MULT ||
	  (GET_CODE (operands[1]) == PLUS &&
	   (GET_CODE (XEXP (operands[1], 0)) == MULT ||
	    GET_CODE (XEXP (operands[1], 1)) == MULT)))
	? \"lda %0,%a1\"
	: \"addu %0,%a1\";
}")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r")
	(match_operand:DF 1 "simple_address_operand" "p"))]
  ""
  "*
{
  FIX_ADDRESS_IF_NEEDED (operands[1]);
  return (GET_CODE (operands[1]) == MULT ||
	  (GET_CODE (operands[1]) == PLUS &&
	   (GET_CODE (XEXP (operands[1], 0)) == MULT ||
	    GET_CODE (XEXP (operands[1], 1)) == MULT)))
	? \"lda.d %0,%a1\"
	: \"addu %0,%a1\";
}")

;;- and instructions (with complement also)
(define_insn "andcbsi3"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(and:SI (match_operand:SI 1 "register_operand" "r")
		(not:SI (match_operand:SI 2 "register_operand" "r"))))]
  ""
  "and.c %0,%1,%2")

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

(define_insn "andsi3"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(and:SI (match_operand:SI 1 "register_operand" "%r")
		(match_operand:SI 2 "arith32_operand" "rn")))]
  ""
  "* return output_and (operands);")

(define_insn ""
  [(set (match_operand:DI 0 "register_operand" "=r")
	(and:SI (match_operand:DI 1 "register_operand" "r")
		(not:SI (match_operand:DI 2 "register_operand" "r"))))]
  ""
  "and.c %d0,%d1,%d2\\n\\tand.c %0,%1,%2")

(define_insn ""
  [(set (match_operand:DI 0 "register_operand" "=r")
	(and:SI (not:SI (match_operand:DI 1 "register_operand" "r"))
		(match_operand:DI 2 "register_operand" "r")))]
  ""
  "and.c %d0,%d2,%d1\\n\\tand.c %0,%2,%1")

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

  xoperands[0] = gen_lowpart (SImode, operands[0]);
  xoperands[1] = gen_lowpart (SImode, operands[1]);
  xoperands[2] = (GET_CODE (operands[2]) == CONST_DOUBLE)
		    ? gen_rtx (CONST_INT, VOIDmode, CONST_DOUBLE_LOW (operands[2]))
		    : gen_lowpart (SImode, operands[2]);

  output_asm_insn (output_and (xoperands), xoperands);

  operands[0] = gen_highpart (SImode, operands[0]);
  operands[1] = gen_highpart (SImode, operands[1]);
  operands[2] = (GET_CODE (operands[2]) == CONST_DOUBLE)
		    ? gen_rtx (CONST_INT, VOIDmode, CONST_DOUBLE_HIGH (operands[2]))
		    : gen_highpart (SImode, operands[2]);

  return output_and (operands);
}")


;;- Bit set (inclusive or) instructions (with complement also)
(define_insn "iorcbsi3"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(ior:SI (match_operand:SI 1 "register_operand" "r")
		(not:SI (match_operand:SI 2 "register_operand" "r"))))]
  ""
  "or.c %0,%1,%2")

(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_insn "iorsi3"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(ior:SI (match_operand:SI 1 "register_operand" "%r")
		(match_operand:SI 2 "arith32_operand" "rn")))]
  ""
  "* output_ior (operands);")

(define_insn ""
  [(set (match_operand:DI 0 "register_operand" "=r")
	(ior:SI (match_operand:DI 1 "register_operand" "r")
		(not:DI (match_operand:SI 2 "register_operand" "r"))))]
  ""
  "or.c %d0,%d1,%d2\\n\\tor.c %0,%1,%2")

(define_insn ""
  [(set (match_operand:DI 0 "register_operand" "=r")
	(ior:SI (not:DI (match_operand:SI 1 "register_operand" "r"))
		(match_operand:DI 2 "register_operand" "r")))]
  ""
  "or.c %d0,%d2,%d1\\n\\tor.c %0,%2,%1")

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

  xoperands[0] = gen_lowpart (SImode, operands[0]);
  xoperands[1] = gen_lowpart (SImode, operands[1]);
  xoperands[2] = (GET_CODE (operands[2]) == CONST_DOUBLE)
		    ? gen_rtx (CONST_INT, VOIDmode, CONST_DOUBLE_LOW (operands[2]))
		    : gen_lowpart (SImode, operands[2]);

  output_asm_insn (output_ior (xoperands), xoperands);

  operands[0] = gen_highpart (SImode, operands[0]);
  operands[1] = gen_highpart (SImode, operands[1]);
  operands[2] = (GET_CODE (operands[2]) == CONST_DOUBLE)
		    ? gen_rtx (CONST_INT, VOIDmode, CONST_DOUBLE_HIGH (operands[2]))
		    : gen_highpart (SImode, operands[2]);

  return output_ior (operands);
}")


;;- xor instructions (with complement also)
(define_insn "xorcbsi3"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(xor:SI (match_operand:SI 1 "register_operand" "r")
		(not:SI (match_operand:SI 2 "register_operand" "r"))))]
  ""
  "xor.c %0,%1,%2")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r")
	(xor:SI (not:SI (match_operand:SI 1 "register_operand" "r"))
		(match_operand:SI 2 "register_operand" "r")))]
  ""
  "xor.c %0,%2,%1")

(define_insn "xorsi3"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(xor:SI (match_operand:SI 1 "register_operand" "%r")
		(match_operand:SI 2 "arith32_operand" "rn")))]
  ""
  "* output_xor (operands);")

(define_insn ""
  [(set (match_operand:DI 0 "register_operand" "=r")
	(xor:SI (match_operand:DI 1 "register_operand" "r")
		(not:SI (match_operand:DI 2 "register_operand" "r"))))]
  ""
  "xor.c %d0,%d1,%d2\\n\\txor.c %0,%1,%2")

(define_insn ""
  [(set (match_operand:DI 0 "register_operand" "=r")
	(xor:SI (not:SI (match_operand:DI 1 "register_operand" "r"))
		(match_operand:DI 2 "register_operand" "r")))]
  ""
  "xor.c %d0,%d2,%d1\\n\\txor.c %0,%2,%1")

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

  xoperands[0] = gen_lowpart (SImode, operands[0]);
  xoperands[1] = gen_lowpart (SImode, operands[1]);
  xoperands[2] = (GET_CODE (operands[2]) == CONST_DOUBLE)
		    ? gen_rtx (CONST_INT, VOIDmode, CONST_DOUBLE_LOW (operands[2]))
		    : gen_lowpart (SImode, operands[2]);

  output_asm_insn (output_xor (xoperands), xoperands);

  operands[0] = gen_highpart (SImode, operands[0]);
  operands[1] = gen_highpart (SImode, operands[1]);
  operands[2] = (GET_CODE (operands[2]) == CONST_DOUBLE)
		    ? gen_rtx (CONST_INT, VOIDmode, CONST_DOUBLE_HIGH (operands[2]))
		    : gen_highpart (SImode, operands[2]);

  return output_xor (operands);
}")


;;- 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:SI (match_operand:DI 1 "register_operand" "r")))]
  ""
  "xor.c %d0,%d1,r0\\n\\txor.c %0,%1,r0")


;; Optimized special cases of shifting.
;; Must precede the general case.

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r")
	(ashiftrt:SI (match_operand:SI 1 "memory_operand" "m")
		     (const_int 24)))]
  "GET_CODE (XEXP (operands[1], 0)) != PLUS
   || (GET_CODE (XEXP (XEXP (operands[1], 0), 0)) != MULT
       && GET_CODE (XEXP (XEXP (operands[1], 0), 1)) != MULT)"
  "*
{
  FIX_ADDRESS_IF_NEEDED (operands[1]);
  return \"ld.b %0,%1\";
}")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r")
	(lshiftrt:SI (match_operand:SI 1 "memory_operand" "m")
		     (const_int 24)))]
  "GET_CODE (XEXP (operands[1], 0)) != PLUS
   || (GET_CODE (XEXP (XEXP (operands[1], 0), 0)) != MULT
       && GET_CODE (XEXP (XEXP (operands[1], 0), 1)) != MULT)"
  "*
{
  FIX_ADDRESS_IF_NEEDED (operands[1]);
  return \"ld.bu %0,%1\";
}")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r")
	(ashiftrt:SI (match_operand:SI 1 "memory_operand" "m")
		     (const_int 16)))]
  "GET_CODE (XEXP (operands[1], 0)) != PLUS
   || (GET_CODE (XEXP (XEXP (operands[1], 0), 0)) != MULT
       && GET_CODE (XEXP (XEXP (operands[1], 0), 1)) != MULT)"
  "*
{
  FIX_ADDRESS_IF_NEEDED (operands[1]);
  return \"ld.h %0,%1\";
}")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r")
	(lshiftrt:SI (match_operand:SI 1 "memory_operand" "m")
		     (const_int 16)))]
  "GET_CODE (XEXP (operands[1], 0)) != PLUS
   || (GET_CODE (XEXP (XEXP (operands[1], 0), 0)) != MULT
       && GET_CODE (XEXP (XEXP (operands[1], 0), 1)) != MULT)"
  "*
{
  FIX_ADDRESS_IF_NEEDED (operands[1]);
  return \"ld.hu %0,%1\";
}")


;; mak optimizations (and of bitmask with shift).

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r")
	(ashift:SI (and:SI (match_operand:SI 1 "register_operand" "%r")
			   (match_operand:SI 2 "mak_mask_operand" "i"))
		   (match_operand:SI 3 "int5_operand" "rI")))]
  ""
  "*
{
  operands[4] = gen_rtx (CONST_INT, SImode, exact_log2 (INTVAL (operands[2]) + 1));
  return \"mak %0,%1,%4<%3>\";
}")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r")
	(lshift:SI (and:SI (match_operand:SI 1 "register_operand" "%r")
			   (match_operand:SI 2 "mak_mask_operand" "i"))
		   (match_operand:SI 3 "int5_operand" "rI")))]
  ""
  "*
{
  operands[4] = gen_rtx (CONST_INT, SImode, exact_log2 (INTVAL (operands[2]) + 1));
  return \"mak %0,%1,%4<%3>\";
}")


;;- arithmetic shift instructions
(define_insn "ashlsi3"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(ashift:SI (match_operand:SI 1 "register_operand" "r")
		   (match_operand:SI 2 "arith_operand" "rI")))]
  ""
  "*
{
  if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) < 32)
    return \"mak %0,%1,0<%2>\";

  if (GET_CODE (operands[2]) == CONST_INT)
    {
      warning (\"Arithmetic shift left by constant < 0 or > 31\");
      if (TARGET_TRAP_LARGE_SHIFT)
	return \"tbnd r0,1\";

      if (TARGET_HANDLE_LARGE_SHIFT)
	return \"or %0,r0,0\\t\\t\\t; was arithmetic shift left by %2\";

      return \"mak %0,%1,%2\";
    }

  if (TARGET_TRAP_LARGE_SHIFT)
    return \"tbnd %2,31\\n\\tmak %0,%1,%2\";

  if (TARGET_HANDLE_LARGE_SHIFT)
    {
      CC_STATUS_INIT_NO_MDEP;
      return \"cmp %!,%2,32\\n\\text %!,%!,1<lo>\\n\\tmak %0,%1,%2\\n\\tand %0,%0,%!\";
    }

  return \"mak %0,%1,%2\";
}")

(define_insn "ashrsi3"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(ashiftrt:SI (match_operand:SI 1 "register_operand" "r")
		     (match_operand:SI 2 "arith_operand" "rI")))]
  ""
  "*
{
  if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) < 32)
    return \"ext %0,%1,0<%2>\";

  if (GET_CODE (operands[2]) == CONST_INT)
    {
      warning (\"Arithmetic shift right by constant < 0 or > 31\");
      if (TARGET_TRAP_LARGE_SHIFT)
	return \"tbnd r0,1\";

      if (TARGET_HANDLE_LARGE_SHIFT)
	return \"ext %0,%1,0<31>\\t\\t\\t; was arithmetic shift right by %2\";

      return \"ext %0,%1,%2\";
    }

  if (TARGET_TRAP_LARGE_SHIFT)
    return \"tbnd %2,31\\n\\text %0,%1,%2\";

  if (TARGET_HANDLE_LARGE_SHIFT)
    {
      CC_STATUS_INIT_NO_MDEP;
      return \"cmp %!,%2,32\\n\\text %!,%!,1<hs>\\n\\tor %!,%!,%2\\n\\text %0,%1,%!\";
    }

  return \"ext %0,%1,%2\";
}")

;;- logical shift instructions
(define_insn "lshlsi3"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(lshift:SI (match_operand:SI 1 "register_operand" "r")
		   (match_operand:SI 2 "arith_operand" "rI")))]
  ""
  "*
{
  if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) < 32)
    return \"mak %0,%1,0<%2>\";

  if (GET_CODE (operands[2]) == CONST_INT)
    {
      warning (\"Logical shift left by constant < 0 or > 31\");
      if (TARGET_TRAP_LARGE_SHIFT)
	return \"tbnd r0,1\";

      if (TARGET_HANDLE_LARGE_SHIFT)
	return \"or %0,r0,0\\t\\t\\t; was logical shift left by %2\";

      return \"mak %0,%1,%2\";
    }

  if (TARGET_TRAP_LARGE_SHIFT)
    return \"tbnd %2,31\\n\\tmak %0,%1,%2\";

  if (TARGET_HANDLE_LARGE_SHIFT)
    {
      CC_STATUS_INIT_NO_MDEP;
      return \"cmp %!,%2,32\\n\\text %!,%!,1<lo>\\n\\tmak %0,%1,%2\\n\\tand %0,%0,%!\";
    }

  return \"mak %0,%1,%2\";
}")

(define_insn "lshrsi3"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(lshiftrt:SI (match_operand:SI 1 "register_operand" "r")
		     (match_operand:SI 2 "arith_operand" "rI")))]
  ""
  "*
{
  if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) < 32)
    return \"extu %0,%1,0<%2>\";

  if (GET_CODE (operands[2]) == CONST_INT)
    {
      warning (\"Arithmetic shift left by constant < 0 or > 31\");
      if (TARGET_TRAP_LARGE_SHIFT)
	return \"tbnd r0,1\";

      if (TARGET_HANDLE_LARGE_SHIFT)
	return \"or %0,r0,0\\t\\t\\t; was logical shift right by %2\";

      return \"extu %0,%1,%2\";
    }

  if (TARGET_TRAP_LARGE_SHIFT)
    return \"tbnd %2,31\\n\\textu %0,%1,%2\";

  if (TARGET_HANDLE_LARGE_SHIFT)
    {
      CC_STATUS_INIT_NO_MDEP;
      return \"cmp %!,%2,32\\n\\text %!,%!,1<lo>\\n\\textu %0,%1,%2\\n\\tand %0,%0,%!\";
    }

  return \"extu %0,%1,%2\";
}")


;;- rotate instructions

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r")
	(rotate:SI (match_operand:SI 1 "register_operand" "r")
		   (match_operand:SI 2 "int5_operand" "K")))]
  ""
  "*
{
  operands[2] = gen_rtx (CONST_INT, SImode, (32 - INTVAL (operands[2])) & 0x1f);
  return \"rot %0,%1,%2\";
}")

(define_insn "rotlsi3"
  [(set (match_operand:SI 0 "register_operand" "=&r")
	(rotate:SI (match_operand:SI 1 "register_operand" "r")
		   (match_operand:SI 2 "register_operand" "r")))]
  ""
  "*
{
  return \"or %0,r0,32\\\n\\tsubu %0,%2,%0\\n\\trot %0,%1,%0\";
}")

(define_insn "rotrsi3"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(rotate:SI (match_operand:SI 1 "register_operand" "r")
		   (match_operand:SI 2 "arith_operand" "rI")))]
  ""
  "rot %0,%1,%2")

;; Special cases of bit-field insns which we should
;; recognize in preference to the general case.
;; These handle aligned 8-bit and 16-bit fields,
;; which can usually be done with move instructions.

(define_insn ""
  [(set (zero_extract:SI (match_operand:SI 0 "memory_operand" "+o")
			 (match_operand:SI 1 "int5_operand" "K")
			 (match_operand:SI 2 "int5_operand" "K"))
	(match_operand:SI 3 "register_operand" "r"))]
  "(INTVAL (operands[1]) == 8 || INTVAL (operands[1]) == 16)
   && INTVAL (operands[2]) % INTVAL (operands[1]) == 0"
  "*
{
  operands[0]
    = adj_offsettable_operand (operands[0], INTVAL (operands[2]) / 8);

  FIX_ADDRESS_IF_NEEDED (operands[0]);
  if (INTVAL (operands[1]) == 8)
    return \"st.b %3,%0\";
  return \"st.h %3,%0\";
}")

(define_insn ""
  [(set (zero_extract:SI (match_operand:SI 0 "memory_operand" "m")
			 (const_int 32)
			 (const_int 0))
	(match_operand:SI 1 "register_operand" "r"))]
  ""
  "*
{
  FIX_ADDRESS_IF_NEEDED (operands[0]);
  return \"st %1,%0\";
}")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=&r")
	(zero_extract:SI (match_operand:SI 1 "memory_operand" "o")
			 (match_operand:SI 2 "int5_operand" "K")
			 (match_operand:SI 3 "int5_operand" "K")))]
  "(INTVAL (operands[2]) == 8 || INTVAL (operands[2]) == 16)
   && INTVAL (operands[3]) % INTVAL (operands[2]) == 0"
  "*
{
  operands[1]
    = adj_offsettable_operand (operands[1], INTVAL (operands[3]) / 8);

  FIX_ADDRESS_IF_NEEDED (operands[1]);
  if (INTVAL (operands[2]) == 8)
    return \"ld.bu %0,%1\";
  return \"ld.hu %0,%1\";
}")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "r")
	(zero_extract:SI (match_operand:SI 1 "memory_operand" "m")
			 (const_int 32)
			 (const_int 0)))]
  ""
  "*
{
  FIX_ADDRESS_IF_NEEDED (operands[1]);
  return \"ld %0,%1\";
}")

(define_insn ""
  [(set (sign_extract:SI (match_operand:SI 0 "memory_operand" "+o")
			 (match_operand:SI 1 "int5_operand" "K")
			 (match_operand:SI 2 "int5_operand" "K"))
	(match_operand:SI 3 "register_operand" "r"))]
  "(INTVAL (operands[1]) == 8 || INTVAL (operands[1]) == 16)
   && INTVAL (operands[2]) % INTVAL (operands[1]) == 0"
  "*
{
  operands[0]
    = adj_offsettable_operand (operands[0], INTVAL (operands[2]) / 8);

  FIX_ADDRESS_IF_NEEDED (operands[0]);
  if (INTVAL (operands[1]) == 8)
    return \"st.b %3,%0\";
  return \"st.h %3,%0\";
}")

(define_insn ""
  [(set (sign_extract:SI (match_operand:SI 0 "memory_operand" "m")
			 (const_int 32)
			 (const_int 0))
	(match_operand:SI 1 "register_operand" "r"))]
  ""
  "*
{
  FIX_ADDRESS_IF_NEEDED (operands[0]);
  return \"st %1,%0\";
}")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r")
	(sign_extract:SI (match_operand:SI 1 "memory_operand" "ro")
			 (match_operand:SI 2 "int5_operand" "K")
			 (match_operand:SI 3 "int5_operand" "K")))]
  "(INTVAL (operands[2]) == 8 || INTVAL (operands[2]) == 16)
   && INTVAL (operands[3]) % INTVAL (operands[2]) == 0"
  "*
{
  operands[1]
    = adj_offsettable_operand (operands[1], INTVAL (operands[3]) / 8);

  FIX_ADDRESS_IF_NEEDED (operands[1]);
  if (INTVAL (operands[2]) == 8)
    return \"ld.b %0,%1\";
  return \"ld.h %0,%1\";
}")

(define_insn ""
  [(set (match_operand:SI 1 "register_operand" "r")
	(sign_extract:SI (match_operand:SI 0 "memory_operand" "m")
			 (const_int 32)
			 (const_int 0)))]
  ""
  "*
{
  FIX_ADDRESS_IF_NEEDED (operands[1]);
  return \"ld %0,%1\";
}")

;; 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)))]
  ""
  "*
{
  return (REGNO (operands[0]) == REGNO (operands[1])) ? \"\" : \"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" "K")
			 (match_operand:SI 3 "int5_operand" "K")))]
  ""
  "*
{
  operands[3] = gen_rtx (CONST_INT, VOIDmode, 32 - (INTVAL(operands[2]) +
						    INTVAL(operands[3])));
  return \"ext %0,%1,%2<%3>\";
}")

(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)))]
  ""
  "*
{
  return (REGNO (operands[0]) == REGNO (operands[1])) ? \"\" : \"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" "K")
			 (match_operand:SI 3 "int5_operand" "K")))]
  ""
  "*
{
  operands[3] = gen_rtx (CONST_INT, VOIDmode, 32 - (INTVAL(operands[2]) +
						    INTVAL(operands[3])));
  return \"extu %0,%1,%2<%3>\";
}")

(define_insn ""
  [(set (zero_extract:SI (match_operand:SI 0 "register_operand" "r")
			 (match_operand:SI 1 "int5_operand" "K")
			 (match_operand:SI 2 "int5_operand" "K"))
	(const_int 0))]
  ""
  "*
{
  operands[2] = gen_rtx (CONST_INT, VOIDmode, 32 - (INTVAL(operands[1]) +
						    INTVAL(operands[2])));
  return \"clr %0,%0,%1<%2>\";
}")

(define_insn ""
  [(set (zero_extract:SI (match_operand:SI 0 "register_operand" "r")
			 (match_operand:SI 1 "int5_operand" "K")
			 (match_operand:SI 2 "int5_operand" "K"))
	(const_int -1))]
  ""
  "*
{
  operands[2] = gen_rtx (CONST_INT, VOIDmode, 32 - (INTVAL(operands[1]) +
						    INTVAL(operands[2])));
  return \"mask %0,%0,%1<%2>\";
}")

(define_insn ""
  [(set (zero_extract:SI (match_operand:SI 0 "register_operand" "=r")
			 (match_operand:SI 1 "int5_operand" "K")
			 (match_operand:SI 2 "int5_operand" "K"))
	(match_operand:SI 3 "int32_operand" "i"))]
  ""
  "*
{
  int value = INTVAL (operands[3]) && (INTVAL (operands[1]) == 32)
	? ~0
	: (1 << INTVAL (operands[1])) - 1;

  operands[2] = gen_rtx (CONST_INT, VOIDmode, 32 - (INTVAL(operands[1]) +
						    INTVAL(operands[2])));

  value <<= INTVAL (operands[2]);
  operands[3] = gen_rtx (CONST_INT, VOIDmode, value);

  if (INT_FITS_16_BITS (value))
    return \"clr %0,%0,%1<%2>\;or %0,%0,%x3\\t\\t\\t; %i3\";
  else if ((value & 0x0000ffff) == 0)
    return \"clr %0,%0,%1<%2>\;or.u %0,%0,hi16(%x3)\\t\\t; %i3\";
  else
    return \"clr %0,%0,%1<%2>\;or.u %0,%0,hi16(%3)\\t\\t; %i3\;or %0,%0,lo16(%3)\";
}")

;; The following doesn't quite work right, deal with it after pass 12
;; (define_expand "insv"
;;   [(set (zero_extract:SI (match_operand:SI 0 "general_operand" "")
;; 
;; 			 (match_operand:SI 1 "int5_operand" "K")
;; 			 (match_operand:SI 2 "int5_operand" "K"))
;; 	(match_operand:SI 3 "general_operand" ""))]
;;   ""
;;   "
;; {
;;   rtx dest = operands[0];
;;   if (! REG_P (dest))
;;     {
;;       extern rtx sequence_stack;
;;       rtx last_insn = XEXP (XEXP (sequence_stack, 1), 0);
;;       rtx last_insn2 = (rtx)0;
;;       rtx last_pat;
;; 
;;       while (last_insn && last_insn != last_insn2 && GET_CODE (last_insn) == NOTE)
;; 	{
;; 	  last_insn2 = last_insn;
;; 	  last_insn == PREV_INSN (last_insn);
;; 	}
;; 
;;       if (last_insn
;; 	  && GET_CODE (last_insn) == INSN
;; 	  && (last_pat = PATTERN (last_insn), 1)
;; 	  && GET_CODE (last_pat) == SET
;; 	  && rtx_equal_p (SET_DEST (last_pat), dest))
;; 	{
;; 	  /* Micro-optimization: if this dest is the same as the previous
;; 	     insn's dest, keep using previous insn's src register.
;; 	     This optimizes the case where several bitfield elements
;; 	     are stored consecutively.  However, PREV_INSN seems to point
;; 	     to itself when debug information is requested.  */
;; 
;; 	  dest = SET_SRC (last_pat);
;; 
;; 	  /* The memory destination may have only gotten a piece of this
;; 	     register, be we want to have the whole thing for the insv pattern.  */
;; 	  if (GET_CODE (dest) == SUBREG)
;; 	    {
;; 	      if (SUBREG_WORD (dest) != 0)
;; 		fatal (\"SUBREG_WORD (dest) != 0 in insv\");
;; 	      dest = SUBREG_REG (dest);
;; 	      if (GET_CODE (dest) != REG)
;; 		fatal (\"GET_CODE (dest) != REG in insv\");
;; 	    }
;; 	}
;;       else if (GET_MODE (dest) != SImode)
;; 	{
;; 	  rtx t = dest;
;; 	  dest = gen_reg_rtx (SImode);
;; 	  convert_move (dest, t, 1);
;; 	}
;;       else
;; 	dest = force_reg (SImode, dest);
;;     }
;; 
;;   if (! REG_P (operands[3])
;;       && operands[3] != const0_rtx
;;       && (GET_CODE (operands[3]) != CONST_INT
;; 	  || INTVAL (operands[3]) != -1))
;;     if (GET_MODE (operands[3]) != SImode
;; 	&& GET_CODE (operands[3]) != CONST_INT)
;;       {
;; 	rtx t = operands[3];
;; 	operands[3] = gen_reg_rtx (SImode);
;; 	convert_move (operands[3], t, 1);
;;       }
;;   else
;;     operands[3] = force_reg (SImode, operands[3]);
;; 
;;   emit_insn (gen_rtx (SET, VOIDmode, gen_rtx (ZERO_EXTRACT, SImode, dest,
;; 							    operands[1],
;; 							    operands[2]),
;; 			   operands[3]));
;; 
;;   if (dest != operands[0])
;;     convert_move (operands[0], dest, 1);
;;   DONE;
;; }")
;; 
;; (define_insn ""
;;   [(set (zero_extract:SI (match_operand:SI 0 "register_operand" "=r")
;; 			 (match_operand:SI 1 "int5_operand" "K")
;; 			 (match_operand:SI 2 "int5_operand" "K"))
;; 	(match_operand:SI 3 "register_operand" "&r"))]
;;   ""
;;   "*
;; {
;;   operands[2] = gen_rtx (CONST_INT, VOIDmode, 31 - INTVAL (operands[2]));
;;   return \"mak %3,%3,%1<%2>\;clr %0,%0,%1<%2>\;or %0,%0,%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")
	(float_truncate:SF (neg:DF (match_operand:DF 1 "register_operand" "r"))))]
  ""
  "fsub.ssd %0,r0,%1")

(define_insn ""
  [(set (match_operand:DF 0 "register_operand" "+r")
	(neg:DF (match_dup 0)))]
  ""
  "xor.u %0,%0,0x8000")

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

(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

(define_expand "abssi2"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(abs:SI (match_operand:SI 1 "register_operand" "r")))]
  ""
  "
{
  extern rtx gen_ashrsi3 (), gen_xorsi3 (), gen_subsi3 ();
  rtx reg_mask = gen_reg_rtx (SImode);
  rtx op1 = force_reg (SImode, operands[1]);

  emit_insn (gen_ashrsi3 (reg_mask, op1,
			  gen_rtx (CONST_INT, VOIDmode, 31)));

  emit_insn (gen_xorsi3 (operands[0], op1, reg_mask));
  emit_insn (gen_subsi3 (operands[0], operands[0], reg_mask));
  DONE;
}")

(define_insn ""
  [(set (match_operand:DF 0 "register_operand" "+r")
	(abs:DF (match_dup 0)))]
  ""
  "and.u %0,%1,0x7fff")

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

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


;; Unconditional jump instruction
(define_insn "jump"
  [(set (pc)
	(label_ref (match_operand 0 "" "")))]
  ""
  "* return output_br_or_call (operands, insn, \"br.n %l0\", \"br %l0\"); ")


;; Subroutines of "casesi".

;; The USE here is so that at least one jump-insn will refer to the label,
;; to keep it alive in jump_optimize.

(define_insn "case_jump"
  [(set (pc) (match_operand:SI 0 "register_operand" "r"))
   (use (label_ref (match_operand 1 "" "")))]
  ""
  "* return output_br_or_call (operands, insn, \"jmp.n %0\", \"jmp %0\"); ")

;; 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 "general_operand" "")
   (match_operand 3 "" "")
   (match_operand 4 "" "")]
  ""
  "
{
  rtx table_elt_addr;
  rtx index_diff;
  rtx index_orig;
  rtx reg;
  rtx gen_subsi3 (), gen_addsi3 (), gen_cmpsi (), gen_bgtu ();
  rtx gen_case_jump ();

  index_orig = force_reg (SImode, operands[0]);
  if (INTVAL (operands[1]) == 0)	/* subtract low bound if != 0 */
    index_diff = index_orig;
  else
    {
      index_diff = gen_reg_rtx (SImode);
      emit_insn (gen_subsi3 (index_diff, index_orig, operands[1]));
    }

  emit_insn (gen_cmpsi (index_diff, operands[2]));
  emit_jump_insn (gen_bgtu (operands[4]));

  /* Construct the address of the index element.  */
  table_elt_addr
    = memory_address_noforce
        (Pmode,
	 gen_rtx (PLUS, Pmode,
		  gen_rtx (MULT, Pmode, index_diff,
			   gen_rtx (CONST_INT, VOIDmode, 4)),
		  gen_rtx (LABEL_REF, VOIDmode, operands[3])));

  reg = gen_reg_rtx (Pmode);
  emit_move_insn (reg, gen_rtx (MEM, Pmode, table_elt_addr));

  /* Jump to the branch which will jump to the appropriate case.  */
  emit_jump_insn (gen_case_jump (reg, operands[3]));
  emit_barrier ();
  DONE;
}")


;; (define_insn "tablejump"
;;  [(set (pc) (match_operand:SI 0 "register_operand" "r"))
;;   (use (label_ref (match_operand 1 "" "")))]
;;  ""
;;  "* return output_br_or_call (operands, insn, \"jmp.n %0\", \"jmp %0\"); ")


;;- jump to subroutine
(define_insn "call"
  [(call (match_operand:SI 0 "memory_operand" "m")
	 (match_operand:SI 1 "immediate_operand" "n"))
   (use (reg:SI 1))]
  ""
  "*
{
  CC_STATUS_INIT;
  operands[2] = XEXP (operands[0], 0);
  if (GET_CODE (operands[2]) == REG)
    return output_br_or_call (operands, insn, \"jsr.n %2\", \"jsr %2\");
  else
    return output_br_or_call (operands, insn, \"bsr.n %2\", \"bsr %2\");
}")

(define_insn "call_value"
  [(set (match_operand 0 "register_operand" "=r")
	(call (match_operand:SI 1 "memory_operand" "m")
	      (match_operand:SI 2 "immediate_operand" "n")))
   (use (reg:SI 1))]
  ""
  "*
{
  CC_STATUS_INIT;
  operands[2] = XEXP (operands[1], 0);
  if (GET_CODE (operands[2]) == REG)
    return output_br_or_call (operands, insn, \"jsr.n %2\", \"jsr %2\");
  else
    return output_br_or_call (operands, insn, \"bsr.n %2\", \"bsr %2\");
}")


;; Nop instruction

(define_insn "nop"
  [(const_int 0)]
  ""
  "or r0,r0,r0\\t\\t\\t; Nop")


;; Peephole to optimize for loops that increment a register, and then
;; test it, to do the test first, and then put the increment into the
;; delay slot

(define_peephole
  [(set (match_operand:SI 0 "register_operand" "=r")
	(match_operator:SI 1 "plus_minus_operator"
			   [(match_dup 0)
			    (match_operand 2 "signed16_operand" "L")]))
   (set (cc0)
	(compare (match_dup 0)
		 (match_operand 3 "int16_operand" "I")))
   (set (pc)
	(if_then_else (match_operator 4 "relop_no_unsigned"
		       [(cc0)
			(const_int 0)])
		      (match_operand 5 "" "")
		      (match_operand 6 "" "")))]
  "flag_delayed_branch
   && dbr_sequence_length () == 0
   && INT_FITS_16_BITS (INTVAL (operands[3]) +
			(((GET_CODE (operands[1]) == MINUS) ? 1 : -1) * INTVAL (operands[2])))"
  "*
{
  static char *add_sub_fmt[2][2] = { { \"addu %0,%0,%x2\\t\\t\\t; %i2\",
				       \"subu %0,%0,%x2\\t\\t\\t; %i2\" },
				     { \"addu %0,%0,%x2\\t\\t\\t; %i2\\n\",
				       \"subu %0,%0,%x2\\t\\t\\t; %i2\\n\" }};

  char *br;
  int num = INTVAL (operands[2]) * ((GET_CODE (operands[1]) == MINUS) ? -1 : 1);
  int pos_num = __builtin_abs (num);

  if (pos_num != INTVAL (operands[2]))
    operands[2] = gen_rtx (CONST_INT, VOIDmode, pos_num);

  operands[7] = gen_rtx (CONST_INT, VOIDmode, INTVAL (operands[3]) - num);

  /* emit the correct branch with the delay slot. */
  if (INTVAL (operands[7]) == 0)
    br = (operands[6] == pc_rtx) ? \"bcnd.n %B4,%0,%5\" : \"bcnd.n %Z4,%0,%6\";
  else
    {
      CC_STATUS_INIT_NO_MDEP;
      br = (operands[6] == pc_rtx)
	? \"cmp %!,%0,%x7\\t\\t\\t; %i7\\n\\tbb1.n %C4,%!,%5\"
	: \"cmp %!,%0,%x7\\t\\t\\t; %i7\\n\\tbb0.n %C4,%!,%6\";
    }

  output_asm_insn (br, operands);

  return add_sub_fmt [(TARGET_REORDER == 0)][(num < 0)];
}")

;; The following peephole, is a special case of the previous one (but
;; are not recogized in general, because the combiner has already
;; elimintated setting cc0 for using bcnd's -- it doesn't win if we
;; convert a two instruction sequence without a delay slot, to a three
;; instruction sequence filling the delay slot), but you do win if you
;; can change a > 0 into a =>.  I'm not sure how often this is useful.

(define_peephole
  [(set (match_operand:SI 0 "register_operand" "=r")
	(match_operator:SI 1 "plus_minus_operator"
			   [(match_dup 0)
			    (match_operand 2 "signed16_operand" "L")]))
   (set (pc)
	(if_then_else (match_operator 3 "relop_no_unsigned"
				      [(match_dup 0)
				       (const_int 0)])
		      (match_operand 4 "" "")
		      (match_operand 5 "" "")))]
  "flag_delayed_branch
   && dbr_sequence_length () == 0
   && ( (GET_CODE (operands[1]) == PLUS && INTVAL (operands[2]) == 1 &&
	 (GET_CODE (operands[3]) == GT && operands[5] == pc_rtx) ||
	 (GET_CODE (operands[3]) == LE && operands[4] == pc_rtx))
       ||
	(GET_CODE (operands[1]) == MINUS && INTVAL (operands[2]) == -1 &&
	 (GET_CODE (operands[3]) == GT && operands[5] == pc_rtx) ||
	 (GET_CODE (operands[3]) == LE && operands[4] == pc_rtx))
       ||
	(GET_CODE (operands[1]) == PLUS && INTVAL (operands[2]) == -1 &&
	 (GET_CODE (operands[3]) == LT && operands[5] == pc_rtx) ||
	 (GET_CODE (operands[3]) == GE && operands[4] == pc_rtx))
       ||
	(GET_CODE (operands[1]) == MINUS && INTVAL (operands[2]) == 1 &&
	 (GET_CODE (operands[3]) == LT && operands[5] == pc_rtx) ||
	 (GET_CODE (operands[3]) == GE && operands[4] == pc_rtx)))"
  "*
{
  static char *add_sub_fmt[2][2] = { { \"addu %0,%0,0x0001\\t\\t\\t; 1\",
				       \"subu %0,%0,0x0001\\t\\t\\t; 1\" },
				     { \"addu %0,%0,0x0001\\t\\t\\t; 1\\n\",
				       \"subu %0,%0,0x0001\\t\\t\\t; 1\\n\" }};

  /* Change the operator to take the lack of increment into account.  */
  switch (GET_CODE (operands[3]))
    {
    case GT: operands[6] = gen_rtx(GE, VOIDmode, const0_rtx, const0_rtx); break;
    case GE: operands[6] = gen_rtx(GT, VOIDmode, const0_rtx, const0_rtx); break;
    case LT: operands[6] = gen_rtx(LE, VOIDmode, const0_rtx, const0_rtx); break;
    case LE: operands[6] = gen_rtx(LT, VOIDmode, const0_rtx, const0_rtx); break;
    default: fatal (\"unknown operator for increment delay slot peephole\");
    }

  /* emit the correct branch with the delay slot. */
  output_asm_insn ((operands[5] == pc_rtx) ? \"bcnd.n %B6,%0,%4\" : \"bcnd.n %Z6,%0,%5\",
		   operands);

  return add_sub_fmt [(TARGET_REORDER == 0)][(GET_CODE (operands[1]) == MINUS)];
}")


;;- Local variables:
;;- mode:emacs-lisp
;;- comment-start: ";;- "
;;- eval: (set-syntax-table (copy-sequence (syntax-table)))
;;- eval: (modify-syntax-entry ?[ "(]")
;;- eval: (modify-syntax-entry ?] ")[")
;;- eval: (modify-syntax-entry ?{ "(}")
;;- eval: (modify-syntax-entry ?} "){")
;;- End:
