;;- Machine description for Intel 860 chip for GNU C compiler
;;   Copyright (C) 1989, 1990 Free Software Foundation, Inc.

;; This file is part of GNU CC.

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

;; GNU CC is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
;; along with GNU CC; see the file COPYING.  If not, write to
;; the Free Software Foundation, 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:

/* Bit-test instructions.  */

(define_insn ""
  [(set (cc0) (eq (and:SI (match_operand:SI 0 "register_operand" "r")
			  (match_operand:SI 1 "logic_operand" "rL"))
		  (const_int 0)))]
  ""
  "*
{
  CC_STATUS_PARTIAL_INIT;
  return \"and %1,%0,%?r0\";
}")

(define_insn ""
  [(set (cc0) (ne (and:SI (match_operand:SI 0 "register_operand" "r")
			  (match_operand:SI 1 "logic_operand" "rL"))
		  (const_int 0)))]
  ""
  "*
{
  CC_STATUS_PARTIAL_INIT;
  cc_status.flags |= CC_NEGATED;
  return \"and %1,%0,%?r0\";
}")

(define_insn ""
  [(set (cc0) (eq (and:SI (match_operand:SI 0 "register_operand" "r")
			  (match_operand:SI 1 "immediate_operand" "i"))
		  (const_int 0)))]
  "GET_CODE (operands[1]) == CONST_INT && (INTVAL (operands[1]) & 0xffff) == 0"
  "*
{
  CC_STATUS_PARTIAL_INIT;
  return \"andh %H1,%0,%?r0\";
}")

(define_insn ""
  [(set (cc0) (ne (and:SI (match_operand:SI 0 "register_operand" "r")
			  (match_operand:SI 1 "immediate_operand" "i"))
		  (const_int 0)))]
  "GET_CODE (operands[1]) == CONST_INT && (INTVAL (operands[1]) & 0xffff) == 0"
  "*
{
  CC_STATUS_PARTIAL_INIT;
  cc_status.flags |= CC_NEGATED;
  return \"andh %H1,%0,%?r0\";
}")

(define_insn ""
  [(set (cc0) (eq (ashiftrt:SI
		   (sign_extend:SI
		    (ashift:QI (match_operand:QI 0 "register_operand" "r")
			       (match_operand:QI 1 "logic_int" "n")))
		   (match_operand:SI 2 "logic_int" "n"))
		  (const_int 0)))]
  ""
  "*
{
  int width = 8 - INTVAL (operands[2]);
  int pos = 8 - width - INTVAL (operands[1]);

  CC_STATUS_PARTIAL_INIT;
  operands[2] = gen_rtx (CONST_INT, VOIDmode,
			 ~((-1) << width) << pos);
  return \"and %2,%0,%?r0\";
}")

;; -------------------------------------------------------------------------
;; SImode signed integer comparisons
;; -------------------------------------------------------------------------

(define_insn "cmpeqsi"
  [(set (cc0) (eq (match_operand:SI 0 "logic_operand" "r,rL")
		  (match_operand:SI 1 "logic_operand" "L,r")))]
  ""
  "*
{
  CC_STATUS_PARTIAL_INIT;
  if (REG_P (operands[0]))
    return \"xor %1,%0,%?r0\";
  else
    return \"xor %0,%1,%?r0\";
}")

(define_insn "cmpnesi"
  [(set (cc0) (ne (match_operand:SI 0 "logic_operand" "r,rL")
		  (match_operand:SI 1 "logic_operand" "L,r")))]
  ""
  "*
{
  CC_STATUS_PARTIAL_INIT;
  cc_status.flags |= CC_NEGATED;
  if (REG_P (operands[0]))
    return \"xor %1,%0,%?r0\";
  else
    return \"xor %0,%1,%?r0\";
}")

(define_insn "cmpltsi"
  [(set (cc0) (lt (match_operand:SI 0 "arith_operand" "r,rI")
		  (match_operand:SI 1 "arith_operand" "I,r")))]
  ""
  "*
{
  CC_STATUS_PARTIAL_INIT;
  if (REG_P (operands[1]))
    return \"subs %0,%1,%?r0\";
  else
    {
      cc_status.flags |= CC_REVERSED;
      operands[1] = gen_rtx (CONST_INT, VOIDmode, - INTVAL (operands[1]));
      return \"adds %1,%0,%?r0\";
    }
}")

(define_insn "cmpgtsi"
  [(set (cc0) (gt (match_operand:SI 0 "arith_operand" "r,rI")
		  (match_operand:SI 1 "arith_operand" "I,r")))]
  ""
  "*
{
  CC_STATUS_PARTIAL_INIT;
  if (REG_P (operands[0]))
    return \"subs %1,%0,%?r0\";
  else
    {
      cc_status.flags |= CC_REVERSED;
      operands[0] = gen_rtx (CONST_INT, VOIDmode, - INTVAL (operands[0]));
      return \"adds %0,%1,%?r0\";
    }
}")

(define_insn "cmplesi"
  [(set (cc0) (le (match_operand:SI 0 "arith_operand" "r,rI")
		  (match_operand:SI 1 "arith_operand" "I,r")))]
  ""
  "*
{
  CC_STATUS_PARTIAL_INIT;
  cc_status.flags |= CC_NEGATED;
  if (REG_P (operands[0]))
    return \"subs %1,%0,%?r0\";
  else
    {
      cc_status.flags |= CC_REVERSED;
      operands[0] = gen_rtx (CONST_INT, VOIDmode, - INTVAL (operands[0]));
      return \"adds %0,%1,%?r0\";
    }
}")

(define_insn "cmpgesi"
  [(set (cc0) (ge (match_operand:SI 0 "arith_operand" "r,rI")
		  (match_operand:SI 1 "arith_operand" "I,r")))]
  ""
  "*
{
  CC_STATUS_PARTIAL_INIT;
  cc_status.flags |= CC_NEGATED;
  if (REG_P (operands[1]))
    return \"subs %0,%1,%?r0\";
  else
    {
      cc_status.flags |= CC_REVERSED;
      operands[1] = gen_rtx (CONST_INT, VOIDmode, - INTVAL (operands[1]));
      return \"adds %1,%0,%?r0\";
    }
}")

;; -------------------------------------------------------------------------
;; SImode unsigned integer comparisons
;; -------------------------------------------------------------------------

;; WARNING!  There is a small i860 hardware limitation (bug?) which we
;; may run up against (if we are not careful) when we are trying to do
;; unsigned comparisons like (x >= 0), (x < 0), (0 <= x), and (0 > x).
;; Specifically, we must avoid using an `addu' instruction to perform
;; such comparisons because the result (in the CC bit register) will
;; come out wrong.  (This fact is documented in a footnote on page 7-10
;; of the 1991 version of the i860 Microprocessor Family Programmer's
;; Reference Manual).  Note that unsigned comparisons of this sort are
;; always redundant anyway, because an unsigned quantity can never be
;; less than zero.  When we see cases like this, we generate an
;; `or K,%r0,%r0' instruction instead (where K is a constant 0 or -1)
;; so as to get the CC bit register set properly for any subsequent
;; conditional jump instruction.

(define_insn "cmpgeusi"
  [(set (cc0) (geu (match_operand:SI 0 "arith_operand" "r,rI")
		   (match_operand:SI 1 "arith_operand" "I,r")))]
  ""
  "*
{
  CC_STATUS_PARTIAL_INIT;
  if (REG_P (operands[1]))
    return \"subu %0,%1,%?r0\";
  else
    {
      if (INTVAL (operands[1]) == 0)
	return \"or 0,%?r0,%?r0\";
      else
	{
	  cc_status.flags |= CC_REVERSED;
	  operands[1] = gen_rtx (CONST_INT, VOIDmode, - INTVAL (operands[1]));
	  return \"addu %1,%0,%?r0\";
	}
    }
}")

(define_insn "cmpleusi"
  [(set (cc0) (leu (match_operand:SI 0 "arith_operand" "r,rI")
		   (match_operand:SI 1 "arith_operand" "I,r")))]
  ""
  "*
{
  CC_STATUS_PARTIAL_INIT;
  if (REG_P (operands[0]))
    return \"subu %1,%0,%?r0\";
  else
    {
      if (INTVAL (operands[0]) == 0)
	return \"or 0,%?r0,%?r0\";
      else
	{
	  cc_status.flags |= CC_REVERSED;
	  operands[0] = gen_rtx (CONST_INT, VOIDmode, - INTVAL (operands[0]));
	  return \"addu %0,%1,%?r0\";
	}
    }
}")

;; -------------------------------------------------------------------------
;; SFmode floating-point comparisons
;; -------------------------------------------------------------------------

(define_insn "cmpeqsf"
  [(set (cc0) (eq (match_operand:SF 0 "reg_or_0_operand" "fG")
		  (match_operand:SF 1 "reg_or_0_operand" "fG")))]
  ""
  "*
{
  CC_STATUS_PARTIAL_INIT;
  return \"pfeq.ss %r0,%r1,%?f0\";
}")

(define_insn "cmpnesf"
  [(set (cc0) (ne (match_operand:SF 0 "reg_or_0_operand" "fG")
		  (match_operand:SF 1 "reg_or_0_operand" "fG")))]
  ""
  "*
{
  CC_STATUS_PARTIAL_INIT;
  cc_status.flags |= CC_NEGATED;
  return \"pfeq.ss %r1,%r0,%?f0\";
}")

;; NOTE:  The i860 Programmer's Reference Manual says that when we are
;; doing (A < B) or (A > B) comparisons, we have to use pfgt for these
;; in order to be IEEE compliant (in case a trap occurs during these
;; operations).  Conversely, for (A <= B) or (A >= B) comparisons, we
;; must use pfle to be IEEE compliant.

(define_insn "cmpltsf"
  [(set (cc0) (lt (match_operand:SF 0 "reg_or_0_operand" "fG")
		  (match_operand:SF 1 "reg_or_0_operand" "fG")))]
  ""
  "*
{
  CC_STATUS_PARTIAL_INIT;
  return \"pfgt.ss %r1,%r0,%?f0\";
}")

(define_insn "cmpgtsf"
  [(set (cc0) (gt (match_operand:SF 0 "reg_or_0_operand" "fG")
		  (match_operand:SF 1 "reg_or_0_operand" "fG")))]
  ""
  "*
{
  CC_STATUS_PARTIAL_INIT;
  return \"pfgt.ss %r0,%r1,%?f0\";
}")

;; NOTE:  The pfle opcode doesn't do what you think it does.  It is
;; bass-ackwards.  It *clears* the CC flag if the first operand is
;; less than or equal to the second.  Thus, we have to set CC_NEGATED
;; for the following two patterns.

(define_insn "cmplesf"
  [(set (cc0) (le (match_operand:SF 0 "reg_or_0_operand" "fG")
		  (match_operand:SF 1 "reg_or_0_operand" "fG")))]
  ""
  "*
{
  CC_STATUS_PARTIAL_INIT;
  cc_status.flags |= CC_NEGATED;
  return \"pfle.ss %r0,%r1,%?f0\";
}")

(define_insn "cmpgesf"
  [(set (cc0) (ge (match_operand:SF 0 "reg_or_0_operand" "fG")
		  (match_operand:SF 1 "reg_or_0_operand" "fG")))]
  ""
  "*
{
  CC_STATUS_PARTIAL_INIT;
  cc_status.flags |= CC_NEGATED;
  return \"pfle.ss %r1,%r0,%?f0\";
}")

;; -------------------------------------------------------------------------
;; DFmode floating-point comparisons
;; -------------------------------------------------------------------------

(define_insn "cmpeqdf"
  [(set (cc0) (eq (match_operand:DF 0 "reg_or_0_operand" "fG")
		  (match_operand:DF 1 "reg_or_0_operand" "fG")))]
  ""
  "*
{
  CC_STATUS_PARTIAL_INIT;
  return \"pfeq.dd %r0,%r1,%?f0\";
}")

(define_insn "cmpnedf"
  [(set (cc0) (ne (match_operand:DF 0 "reg_or_0_operand" "fG")
		  (match_operand:DF 1 "reg_or_0_operand" "fG")))]
  ""
  "*
{
  CC_STATUS_PARTIAL_INIT;
  cc_status.flags |= CC_NEGATED;
  return \"pfeq.dd %r1,%r0,%?f0\";
}")

;; NOTE:  The i860 Programmer's Reference Manual says that when we are
;; doing (A < B) or (A > B) comparisons, we have to use pfgt for these
;; in order to be IEEE compliant (in case a trap occurs during these
;; operations).  Conversely, for (A <= B) or (A >= B) comparisons, we
;; must use pfle to be IEEE compliant.

(define_insn "cmpltdf"
  [(set (cc0) (lt (match_operand:DF 0 "reg_or_0_operand" "fG")
		  (match_operand:DF 1 "reg_or_0_operand" "fG")))]
  ""
  "*
{
  CC_STATUS_PARTIAL_INIT;
  return \"pfgt.dd %r1,%r0,%?f0\";
}")

(define_insn "cmpgtdf"
  [(set (cc0) (gt (match_operand:DF 0 "reg_or_0_operand" "fG")
		  (match_operand:DF 1 "reg_or_0_operand" "fG")))]
  ""
  "*
{
  CC_STATUS_PARTIAL_INIT;
  return \"pfgt.dd %r0,%r1,%?f0\";
}")

;; NOTE:  The pfle opcode doesn't do what you think it does.  It is
;; bass-ackwards.  It *clears* the CC flag if the first operand is
;; less than or equal to the second.  Thus, we have to set CC_NEGATED
;; for the following two patterns.

(define_insn "cmpledf"
  [(set (cc0) (le (match_operand:DF 0 "reg_or_0_operand" "fG")
		  (match_operand:DF 1 "reg_or_0_operand" "fG")))]
  ""
  "*
{
  CC_STATUS_PARTIAL_INIT;
  cc_status.flags |= CC_NEGATED;
  return \"pfle.dd %r0,%r1,%?f0\";
}")

(define_insn "cmpgedf"
  [(set (cc0) (ge (match_operand:DF 0 "reg_or_0_operand" "fG")
		  (match_operand:DF 1 "reg_or_0_operand" "fG")))]
  ""
  "*
{
  CC_STATUS_PARTIAL_INIT;
  cc_status.flags |= CC_NEGATED;
  return \"pfle.dd %r1,%r0,%?f0\";
}")

;; ------------------------------------------------------------------------
;; Integer EQ/NE comparisons against constant values which will fit in the
;; 16-bit immediate field of an instruction.  These are made by combining.
;; ------------------------------------------------------------------------

(define_insn ""
  [(set (cc0) (eq (zero_extend:SI (match_operand:HI 0 "load_operand" "m"))
	          (match_operand:SI 1 "small_int" "I")))]
  "INTVAL (operands[1]) >= 0"
  "*
{
  CC_STATUS_PARTIAL_INIT;
  return \"ld.s %0,%?r31\;xor %1,%?r31,%?r0\";
}")

(define_insn ""
  [(set (cc0) (eq (match_operand:SI 0 "small_int" "I")
	          (zero_extend:SI (match_operand:HI 1 "load_operand" "m"))))]
  "INTVAL (operands[0]) >= 0"
  "*
{
  CC_STATUS_PARTIAL_INIT;
  return \"ld.s %1,%?r31\;xor %0,%?r31,%?r0\";
}")

;; ------------------------------------------------------------------------
;; Define the real conditional branch instructions.
;; ------------------------------------------------------------------------

(define_insn "cbranch"
  [(set (pc) (if_then_else (eq (cc0) (const_int 0))
			   (label_ref (match_operand 0 "" ""))
			   (pc)))]
  ""
  "*
{
  if ((cc_prev_status.flags & CC_NEGATED) == 0)
    return \"bnc %l0\";
  else
    return \"bc %l0\";
}")

(define_insn "flipped_cbranch"
  [(set (pc) (if_then_else (ne (cc0)
			       (const_int 0))
			   (pc)
			   (label_ref (match_operand 0 "" ""))))]
  ""
  "*
{
  if ((cc_prev_status.flags & CC_NEGATED) == 0)
    return \"bnc %l0\";
  else
    return \"bc %l0\";
}")

(define_insn "inverse_cbranch"
  [(set (pc) (if_then_else (eq (cc0)
			       (const_int 0))
			   (pc)
			   (label_ref (match_operand 0 "" ""))))]
  ""
  "*
{
  if ((cc_prev_status.flags & CC_NEGATED) == 0)
    return \"bc %l0\";
  else
    return \"bnc %l0\";
}")


(define_insn "flipped_inverse_cbranch"
  [(set (pc) (if_then_else (ne (cc0)
			       (const_int 0))
			   (label_ref (match_operand 0 "" ""))
			   (pc)))]
  ""
  "*
{
  if ((cc_prev_status.flags & CC_NEGATED) == 0)
    return \"bc %l0\";
  else
    return \"bnc %l0\";
}")

;; Simple BTE/BTNE compare-and-branch insns made by combining.
;; Note that it is wrong to add similar patterns for QI or HImode
;; because bte/btne always compare the whole register.

(define_insn ""
  [(set (pc)
        (if_then_else (eq (match_operand:SI 0 "register_operand" "r")
			  (match_operand:SI 1 "bte_operand" "rK"))
		      (label_ref (match_operand 2 "" ""))
		      (pc)))]
  ""
  "bte %1,%0,%2")

(define_insn ""
  [(set (pc)
        (if_then_else (ne (match_operand:SI 0 "register_operand" "r")
			  (match_operand:SI 1 "bte_operand" "rK"))
		      (label_ref (match_operand 2 "" ""))
		      (pc)))]
  ""
  "btne %1,%0,%2")

(define_insn ""
  [(set (pc)
        (if_then_else (eq (match_operand:SI 0 "register_operand" "r")
			  (match_operand:SI 1 "bte_operand" "rK"))
		      (pc)
		      (label_ref (match_operand 2 "" ""))))]
  ""
  "btne %1,%0,%2")

(define_insn ""
  [(set (pc)
        (if_then_else (ne (match_operand:SI 0 "register_operand" "r")
			  (match_operand:SI 1 "bte_operand" "rK"))
		      (pc)
		      (label_ref (match_operand 2 "" ""))))]
  ""
  "bte %1,%0,%2")

;; Load byte/halfword, zero-extend, & compare-and-branch insns.
;; These are made by combining.

(define_insn ""
  [(set (pc)
        (if_then_else (eq (zero_extend:SI (match_operand:QI 0 "memory_operand" "m"))
                          (match_operand:SI 1 "bte_operand" "K"))
                      (label_ref (match_operand 2 "" ""))
                      (pc)))
   (match_scratch:SI 3 "=r")]
  ""
  "ld.b %0,%3;bte %1,%3,%2")

(define_insn ""
  [(set (pc)
        (if_then_else (ne (zero_extend:SI (match_operand:QI 0 "memory_operand" "m"))
                          (match_operand:SI 1 "bte_operand" "K"))
                      (label_ref (match_operand 2 "" ""))
                      (pc)))
   (match_scratch:SI 3 "=r")]
  ""
  "ld.b %0,%3;btne %1,%3,%2")

(define_insn ""
  [(set (pc)
        (if_then_else (eq (zero_extend:SI (match_operand:QI 0 "memory_operand" "m"))
                          (match_operand:SI 1 "bte_operand" "K"))
                      (pc)
                      (label_ref (match_operand 2 "" ""))))
   (match_scratch:SI 3 "=r")]
  ""
  "ld.b %0,%3;btne %1,%3,%2")

(define_insn ""
  [(set (pc)
        (if_then_else (ne (zero_extend:SI (match_operand:QI 0 "memory_operand" "m"))
                          (match_operand:SI 1 "bte_operand" "K"))
                      (pc)
                      (label_ref (match_operand 2 "" ""))))
   (match_scratch:SI 3 "=r")]
  ""
  "ld.b %0,%3;bte %1,%3,%2")

(define_insn ""
  [(set (pc)
        (if_then_else (eq (zero_extend:SI (match_operand:HI 0 "memory_operand" "m"))
                          (match_operand:SI 1 "bte_operand" "K"))
                      (label_ref (match_operand 2 "" ""))
                      (pc)))
   (match_scratch:SI 3 "=r")]
  ""
  "ld.s %0,%3;bte %1,%3,%2")

(define_insn ""
  [(set (pc)
        (if_then_else (ne (zero_extend:SI (match_operand:HI 0 "memory_operand" "m"))
                          (match_operand:SI 1 "bte_operand" "K"))
                      (label_ref (match_operand 2 "" ""))
                      (pc)))
   (match_scratch:SI 3 "=r")]
  ""
  "ld.s %0,%3;btne %1,%3,%2")

(define_insn ""
  [(set (pc)
        (if_then_else (eq (zero_extend:SI (match_operand:HI 0 "memory_operand" "m"))
                          (match_operand:SI 1 "bte_operand" "K"))
                      (pc)
                      (label_ref (match_operand 2 "" ""))))
   (match_scratch:SI 3 "=r")]
  ""
  "ld.s %0,%3;btne %1,%3,%2")

(define_insn ""
  [(set (pc)
        (if_then_else (ne (zero_extend:SI (match_operand:HI 0 "memory_operand" "m"))
                          (match_operand:SI 1 "bte_operand" "K"))
                      (pc)
                      (label_ref (match_operand 2 "" ""))))
   (match_scratch:SI 3 "=r")]
  ""
  "ld.s %0,%3;bte %1,%3,%2")


;; Generation of conditionals.

;; We save the compare operands in the cmpxx patterns and use then when
;; we generate the branch.

(define_expand "cmpsi"
  [(set (cc0) (compare (match_operand:SI 0 "register_operand" "")
		       (match_operand:SI 1 "compare_operand" "")))]
  ""
  "
{ i860_compare_op0 = operands[0];
  i860_compare_op1 = operands[1];
  DONE;
}")

(define_expand "cmpsf"
  [(set (cc0) (compare (match_operand:SF 0 "register_operand" "")
		       (match_operand:SF 1 "register_operand" "")))]
  ""
  "
{ i860_compare_op0 = operands[0];
  i860_compare_op1 = operands[1];
  DONE;
}")

(define_expand "cmpdf"
  [(set (cc0) (compare (match_operand:DF 0 "register_operand" "")
		       (match_operand:DF 1 "register_operand" "")))]
  ""
  "
{ i860_compare_op0 = operands[0];
  i860_compare_op1 = operands[1];
  DONE;
}")

;; These are the standard-named conditional branch patterns.
;; Detailed comments are found in the first one only.

(define_expand "beq"
  [(set (pc)
	(if_then_else (eq (cc0)
			  (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "
{
  /* Emit a single-condition compare insn according to
     the type of operands and the condition to be tested.  */

  if (GET_MODE_CLASS (GET_MODE (i860_compare_op0)) == MODE_INT)
    emit_insn (gen_cmpeqsi (i860_compare_op0, i860_compare_op1));
  else if (GET_MODE (i860_compare_op0) == SFmode)
    emit_insn (gen_cmpeqsf (i860_compare_op0, i860_compare_op1));
  else if (GET_MODE (i860_compare_op0) == DFmode)
    emit_insn (gen_cmpeqdf (i860_compare_op0, i860_compare_op1));
  else
    abort ();

  /* Emit branch-if-true.  */

  emit_jump_insn (gen_flipped_inverse_cbranch (operands[0]));
  DONE;
}")

(define_expand "bne"
  [(set (pc)
	(if_then_else (ne (cc0)
			  (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "
{
  if (GET_MODE_CLASS (GET_MODE (i860_compare_op0)) == MODE_INT)
    emit_insn (gen_cmpeqsi (i860_compare_op0, i860_compare_op1));
  else if (GET_MODE (i860_compare_op0) == SFmode)
    emit_insn (gen_cmpeqsf (i860_compare_op0, i860_compare_op1));
  else if (GET_MODE (i860_compare_op0) == DFmode)
    emit_insn (gen_cmpeqdf (i860_compare_op0, i860_compare_op1));
  else
    abort ();

  emit_jump_insn (gen_flipped_cbranch (operands[0]));

  DONE;
}")

(define_expand "bgt"
  [(set (pc)
	(if_then_else (gt (cc0)
			  (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "
{
  if (GET_MODE_CLASS (GET_MODE (i860_compare_op0)) == MODE_INT)
    emit_insn (gen_cmpgtsi (i860_compare_op0, i860_compare_op1));
  else if (GET_MODE (i860_compare_op0) == SFmode)
    emit_insn (gen_cmpgtsf (i860_compare_op0, i860_compare_op1));
  else if (GET_MODE (i860_compare_op0) == DFmode)
    emit_insn (gen_cmpgtdf (i860_compare_op0, i860_compare_op1));
  else
    abort ();

  emit_jump_insn (gen_flipped_inverse_cbranch (operands[0]));
  DONE;
}")

(define_expand "blt"
  [(set (pc)
	(if_then_else (lt (cc0)
			  (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "
{
  if (GET_MODE_CLASS (GET_MODE (i860_compare_op0)) == MODE_INT)
    emit_insn (gen_cmpltsi (i860_compare_op0, i860_compare_op1));
  else if (GET_MODE (i860_compare_op0) == SFmode)
    emit_insn (gen_cmpltsf (i860_compare_op0, i860_compare_op1));
  else if (GET_MODE (i860_compare_op0) == DFmode)
    emit_insn (gen_cmpltdf (i860_compare_op0, i860_compare_op1));
  else
    abort ();

  emit_jump_insn (gen_flipped_inverse_cbranch (operands[0]));
  DONE;
}")

(define_expand "ble"
  [(set (pc)
	(if_then_else (le (cc0)
			  (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "
{
  if (GET_MODE_CLASS (GET_MODE (i860_compare_op0)) == MODE_INT)
    {
      emit_insn (gen_cmpgtsi (i860_compare_op0, i860_compare_op1));
      emit_jump_insn (gen_flipped_cbranch (operands[0]));
    }
  else
    {
      if (GET_MODE (i860_compare_op0) == SFmode)
	emit_insn (gen_cmplesf (i860_compare_op0, i860_compare_op1));
      else if (GET_MODE (i860_compare_op0) == DFmode)
	emit_insn (gen_cmpledf (i860_compare_op0, i860_compare_op1));
      else
	abort ();
      emit_jump_insn (gen_flipped_inverse_cbranch (operands[0]));
    }
  DONE;
}")

(define_expand "bge"
  [(set (pc)
	(if_then_else (ge (cc0)
			  (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "
{
  if (GET_MODE_CLASS (GET_MODE (i860_compare_op0)) == MODE_INT)
    {
      emit_insn (gen_cmpltsi (i860_compare_op0, i860_compare_op1));
      emit_jump_insn (gen_flipped_cbranch (operands[0]));
    }
  else
    {
      if (GET_MODE (i860_compare_op0) == SFmode)
	emit_insn (gen_cmpgesf (i860_compare_op0, i860_compare_op1));
      else if (GET_MODE (i860_compare_op0) == DFmode)
	emit_insn (gen_cmpgedf (i860_compare_op0, i860_compare_op1));
      else
	abort ();
      emit_jump_insn (gen_flipped_inverse_cbranch (operands[0]));
    }
  DONE;
}")

(define_expand "bgtu"
  [(set (pc)
	(if_then_else (gtu (cc0)
			   (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "
{
  if (GET_MODE_CLASS (GET_MODE (i860_compare_op0)) != MODE_INT)
    abort ();

  emit_insn (gen_cmpleusi (i860_compare_op0, i860_compare_op1));
  emit_jump_insn (gen_flipped_cbranch (operands[0]));
  DONE;
}")

(define_expand "bltu"
  [(set (pc)
	(if_then_else (ltu (cc0)
			   (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "
{
  if (GET_MODE_CLASS (GET_MODE (i860_compare_op0)) != MODE_INT)
    abort ();

  emit_insn (gen_cmpgeusi (i860_compare_op0, i860_compare_op1));
  emit_jump_insn (gen_flipped_cbranch (operands[0]));
  DONE;
}")

(define_expand "bgeu"
  [(set (pc)
	(if_then_else (geu (cc0)
			   (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "
{
  if (GET_MODE_CLASS (GET_MODE (i860_compare_op0)) != MODE_INT)
    abort ();

  emit_insn (gen_cmpgeusi (i860_compare_op0, i860_compare_op1));
  emit_jump_insn (gen_flipped_inverse_cbranch (operands[0]));
  DONE;
}")

(define_expand "bleu"
  [(set (pc)
	(if_then_else (leu (cc0)
			   (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "
{
  if (GET_MODE_CLASS (GET_MODE (i860_compare_op0)) != MODE_INT)
    abort ();

  emit_insn (gen_cmpleusi (i860_compare_op0, i860_compare_op1));
  emit_jump_insn (gen_flipped_inverse_cbranch (operands[0]));
  DONE;
}")

;; Move instructions

;; Note that source operands for `mov' pseudo-instructions are no longer
;; allowed (by the svr4 assembler) to be "big" things, i.e. constants that
;; won't fit in 16-bits.  (This includes any sort of a relocatable address
;; also.)  Thus, we must use an explicit orh/or pair of instructions if
;; the source operand is something "big".

(define_insn "movsi"
  [(set (match_operand:SI 0 "general_operand" "=r,m,f")
	(match_operand:SI 1 "general_operand" "rmif,rfJ,rmfJ"))]
  ""
  "*
{
  if (GET_CODE (operands[0]) == MEM)
    {
      if (CONSTANT_ADDRESS_P (XEXP (operands[0], 0)))
	return output_store (operands);
      if (FP_REG_P (operands[1]))
	return \"fst.l %1,%0\";
      return \"st.l %r1,%0\";
    }
  if (GET_CODE (operands[1]) == MEM)
    {
      if (CONSTANT_ADDRESS_P (XEXP (operands[1], 0)))
	return output_load (operands);
      if (FP_REG_P (operands[0]))
	return \"fld.l %1,%0\";
      return \"ld.l %1,%0\";
    }
  if (FP_REG_P (operands[1]) && FP_REG_P (operands[0]))
    return \"fmov.ss %1,%0\";
  if (FP_REG_P (operands[1]))
    return \"fxfr %1,%0\";
  if (FP_REG_P (operands[0]) && operands[1] == const0_rtx)
    return \"fmov.ss %?f0,%0\";
  if (FP_REG_P (operands[0]))
    return \"ixfr %1,%0\";

  if (GET_CODE (operands[1]) == REG)
    return \"shl %?r0,%1,%0\";

  CC_STATUS_PARTIAL_INIT;

  if (GET_CODE (operands[1]) == CONST_INT)
    {
      if((INTVAL (operands[1]) & 0xffff0000) == 0)
        return \"or %L1,%?r0,%0\";
      if((INTVAL (operands[1]) & 0x0000ffff) == 0)
        return \"orh %H1,%?r0,%0\";
    }
  return \"orh %H1,%?r0,%0\;or %L1,%0,%0\";
}")
 
(define_insn "movhi"
  [(set (match_operand:HI 0 "general_operand" "=r,m,!*f,!r")
	(match_operand:HI 1 "general_operand" "rmi,rJ,rJ*f,*f"))]
  ""
  "*
{
  if (GET_CODE (operands[0]) == MEM)
    {
      if (CONSTANT_ADDRESS_P (XEXP (operands[0], 0)))
	return output_store (operands);
      return \"st.s %r1,%0\";
    }
  if (GET_CODE (operands[1]) == MEM)
    {
      if (CONSTANT_ADDRESS_P (XEXP (operands[1], 0)))
	return output_load (operands);
      return \"ld.s %1,%0\";
    }
  if (FP_REG_P (operands[1]) && FP_REG_P (operands[0]))
    return \"fmov.ss %1,%0\";
  if (FP_REG_P (operands[1]))
    return \"fxfr %1,%0\";
  if (FP_REG_P (operands[0]) && operands[1] == const0_rtx)
    return \"fmov.ss %?f0,%0\";
  if (FP_REG_P (operands[0]))
    return \"ixfr %1,%0\";

  if (GET_CODE (operands[1]) == REG)
    return \"shl %?r0,%1,%0\";

  CC_STATUS_PARTIAL_INIT;

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

(define_insn "movqi"
  [(set (match_operand:QI 0 "general_operand" "=r,m,!*f,!r")
	(match_operand:QI 1 "general_operand" "rmi,rJ,rJ*f,*f"))]
  ""
  "*
{
  if (GET_CODE (operands[0]) == MEM)
    {
      if (CONSTANT_ADDRESS_P (XEXP (operands[0], 0)))
	return output_store (operands);
      return \"st.b %r1,%0\";
    }
  if (GET_CODE (operands[1]) == MEM)
    {
      if (CONSTANT_ADDRESS_P (XEXP (operands[1], 0)))
	return output_load (operands);
      return \"ld.b %1,%0\";
    }
  if (FP_REG_P (operands[1]) && FP_REG_P (operands[0]))
    return \"fmov.ss %1,%0\";
  if (FP_REG_P (operands[1]))
    return \"fxfr %1,%0\";
  if (FP_REG_P (operands[0]) && operands[1] == const0_rtx)
    return \"fmov.ss %?f0,%0\";
  if (FP_REG_P (operands[0]))
    return \"ixfr %1,%0\";

  if (GET_CODE (operands[1]) == REG)
    return \"shl %?r0,%1,%0\";

  CC_STATUS_PARTIAL_INIT;

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

;; The definition of this insn does not really explain what it does,
;; but it should suffice
;; that anything generated as this insn will be recognized as one
;; and that it won't successfully combine with anything.
(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 "nonmemory_operand" ""))
	      (use (match_operand:SI 3 "immediate_operand" ""))
	      (clobber (match_dup 4))
	      (clobber (match_dup 5))
	      (clobber (match_dup 6))
	      (clobber (match_dup 0))
	      (clobber (match_dup 1))])]
  ""
  "
{
  operands[0] = copy_to_mode_reg (SImode, XEXP (operands[0], 0));
  operands[1] = copy_to_mode_reg (SImode, XEXP (operands[1], 0));
  operands[4] = gen_reg_rtx (SImode);
  operands[5] = gen_reg_rtx (SImode);
  operands[6] = gen_reg_rtx (SImode);
}")

(define_insn ""
  [(set (mem:BLK (match_operand:SI 0 "register_operand" "r"))
	(mem:BLK (match_operand:SI 1 "register_operand" "r")))
   (use (match_operand:SI 2 "general_operand" "rn"))
   (use (match_operand:SI 3 "immediate_operand" "i"))
   (clobber (match_operand:SI 4 "register_operand" "=r"))
   (clobber (match_operand:SI 5 "register_operand" "=r"))
   (clobber (match_operand:SI 6 "register_operand" "=r"))
   (clobber (match_dup 0))
   (clobber (match_dup 1))]
  ""
  "* return output_block_move (operands);")

;; Floating point move insns

;; This pattern forces (set (reg:DF ...) (const_double ...))
;; to be reloaded by putting the constant into memory.
;; It must come before the more general movdf pattern.
(define_insn ""
  [(set (match_operand:DF 0 "general_operand" "=r,f,o")
	(match_operand:DF 1 "" "mG,m,G"))]
  "GET_CODE (operands[1]) == CONST_DOUBLE"
  "*
{
  if (FP_REG_P (operands[0]) || operands[1] == CONST0_RTX (DFmode))
    return output_fp_move_double (operands);
  return output_move_double (operands);
}")

(define_insn "movdf"
  [(set (match_operand:DF 0 "general_operand" "=*rm,&*r,?f,?*rm")
	(match_operand:DF 1 "general_operand" "*r,m,*rfmG,f"))]
  ""
  "*
{
  if (GET_CODE (operands[0]) == MEM
      && CONSTANT_ADDRESS_P (XEXP (operands[0], 0)))
    return output_store (operands);
  if (GET_CODE (operands[1]) == MEM
      && CONSTANT_ADDRESS_P (XEXP (operands[1], 0)))
    return output_load (operands);

  if (FP_REG_P (operands[0]) || FP_REG_P (operands[1]))
    return output_fp_move_double (operands);
  return output_move_double (operands);
}")

(define_insn "movdi"
  [(set (match_operand:DI 0 "general_operand" "=rm,&r,?f,?rm")
	(match_operand:DI 1 "general_operand" "r,miF,rfmG,f"))]
  ""
  "*
{
  if (GET_CODE (operands[0]) == MEM
      && CONSTANT_ADDRESS_P (XEXP (operands[0], 0)))
    return output_store (operands);
  if (GET_CODE (operands[1]) == MEM
      && CONSTANT_ADDRESS_P (XEXP (operands[1], 0)))
    return output_load (operands);

  /* ??? How can we have a DFmode arg here with DImode above? */
  if (FP_REG_P (operands[0]) && operands[1] == CONST0_RTX (DFmode))
    return \"fmov.dd %?f0,%0\";

  if (FP_REG_P (operands[0]) || FP_REG_P (operands[1]))
    return output_fp_move_double (operands);
  return output_move_double (operands);
}")

;; The alternative m/r is separate from m/f
;; The first alternative is separate from the second for the same reason.
(define_insn "movsf"
  [(set (match_operand:SF 0 "general_operand" "=*rf,*rf,*r,m,m")
	(match_operand:SF 1 "general_operand" "*r,fmG,F,*r,f"))]
  ""
  "*
{
  if (GET_CODE (operands[0]) == MEM
      && CONSTANT_ADDRESS_P (XEXP (operands[0], 0)))
    return output_store (operands);
  if (GET_CODE (operands[1]) == MEM
      && CONSTANT_ADDRESS_P (XEXP (operands[1], 0)))
    return output_load (operands);
  if (FP_REG_P (operands[0]))
    {
      if (FP_REG_P (operands[1]))
	return \"fmov.ss %1,%0\";
      if (GET_CODE (operands[1]) == REG)
	return \"ixfr %1,%0\";
      if (operands[1] == CONST0_RTX (SFmode))
        return \"fmov.ss %?f0,%0\";
      if (CONSTANT_ADDRESS_P (XEXP (operands[1], 0)))
	{
	  if (! ((cc_prev_status.flags & CC_KNOW_HI_R31)
		 && (cc_prev_status.flags & CC_HI_R31_ADJ)
		 && cc_prev_status.mdep == XEXP(operands[1],0)))
	    {
	      CC_STATUS_INIT;
	      cc_status.flags |= CC_KNOW_HI_R31 | CC_HI_R31_ADJ;
	      cc_status.mdep = XEXP (operands[1], 0);
	      return \"orh %h1,%?r0,%?r31\;fld.l %L1(%?r31),%0\";
	    }
	  return \"fld.l %L1(%?r31),%0\";
	}
      return \"fld.l %1,%0\";
    }
  if (FP_REG_P (operands[1]) || GET_CODE (operands[1]) == CONST_DOUBLE)
    {
      if (GET_CODE (operands[0]) == REG && FP_REG_P (operands[1]))
	return \"fxfr %1,%0\";
      if (GET_CODE (operands[0]) == REG)
	{
	  CC_STATUS_PARTIAL_INIT;
	  if (GET_CODE (operands[1]) == CONST_DOUBLE)
	    {
	      register unsigned long ul;

              ul = sfmode_constant_to_ulong (operands[1]);
	      if ((ul & 0x0000ffff) == 0)
		return \"orh %H1,%?r0,%0\";
	      if ((ul & 0xffff0000) == 0)
		return \"or %L1,%?r0,%0\";
	    }
          return \"orh %H1,%?r0,%0\;or %L1,%0,%0\";
	}
      /* Now operand 0 must be memory.
         If operand 1 is CONST_DOUBLE, its value must be 0.  */
      if (CONSTANT_ADDRESS_P (XEXP (operands[0], 0)))
	{
	  if (! ((cc_prev_status.flags & CC_KNOW_HI_R31)
		 && (cc_prev_status.flags & CC_HI_R31_ADJ)
		 && XEXP (operands[0], 0) == cc_prev_status.mdep))
	    {
	      CC_STATUS_INIT;
	      cc_status.flags |= CC_KNOW_HI_R31 | CC_HI_R31_ADJ;
	      cc_status.mdep = XEXP (operands[0], 0);
	      output_asm_insn (\"orh %h0,%?r0,%?r31\", operands);
	    }
	  return \"fst.l %r1,%L0(%?r31)\";
	}
      return \"fst.l %r1,%0\";
    }
  if (GET_CODE (operands[0]) == MEM)
    return \"st.l %r1,%0\";
  if (GET_CODE (operands[1]) == MEM)
    return \"ld.l %1,%0\";
  if (operands[1] == CONST0_RTX (SFmode))
    return \"shl %?r0,%?r0,%0\";
  return \"mov %1,%0\";
}")

;; Special load insns for REG+REG addresses.
;; Such addresses are not "legitimate" because st rejects them.

(define_insn ""
  [(set (match_operand:DF 0 "register_operand" "=rf")
	(match_operand:DF 1 "indexed_operand" "m"))]
  ""
  "*
{
  if (FP_REG_P (operands[0]))
    return output_fp_move_double (operands);
  return output_move_double (operands);
}")

(define_insn ""
  [(set (match_operand:SF 0 "register_operand" "=rf")
	(match_operand:SF 1 "indexed_operand" "m"))]
  ""
  "*
{
  if (FP_REG_P (operands[0]))
    return \"fld.l %1,%0\";
  return \"ld.l %1,%0\";
}")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=rf")
	(match_operand:SI 1 "indexed_operand" "m"))]
  ""
  "*
{
  if (FP_REG_P (operands[0]))
    return \"fld.l %1,%0\";
  return \"ld.l %1,%0\";
}")

(define_insn ""
  [(set (match_operand:HI 0 "register_operand" "=r")
	(match_operand:HI 1 "indexed_operand" "m"))]
  ""
  "ld.s %1,%0")

(define_insn ""
  [(set (match_operand:QI 0 "register_operand" "=r")
	(match_operand:QI 1 "indexed_operand" "m"))]
  ""
  "ld.b %1,%0")

;; Likewise for floating-point store insns.

(define_insn ""
  [(set (match_operand:DF 0 "indexed_operand" "=m")
	(match_operand:DF 1 "register_operand" "f"))]
  ""
  "fst.d %1,%0")

(define_insn ""
  [(set (match_operand:SF 0 "indexed_operand" "=m")
	(match_operand:SF 1 "register_operand" "f"))]
  ""
  "fst.l %1,%0")

;;- truncation instructions
(define_insn "truncsiqi2"
  [(set (match_operand:QI 0 "general_operand" "=g")
	(truncate:QI
	 (match_operand:SI 1 "register_operand" "r")))]
  ""
  "*
{
  if (GET_CODE (operands[0]) == MEM)
    if (CONSTANT_ADDRESS_P (XEXP (operands[0], 0)))
      {
	if (! ((cc_prev_status.flags & CC_KNOW_HI_R31)
	       && (cc_prev_status.flags & CC_HI_R31_ADJ)
	       && XEXP (operands[0], 0) == cc_prev_status.mdep))
	  {
	    CC_STATUS_INIT;
	    cc_status.flags |= CC_KNOW_HI_R31 | CC_HI_R31_ADJ;
	    cc_status.mdep = XEXP (operands[0], 0);
	    output_asm_insn (\"orh %h0,%?r0,%?r31\", operands);
	  }
	return \"st.b %1,%L0(%?r31)\";
      }
    else
      return \"st.b %1,%0\";
  return \"shl %?r0,%1,%0\";
}")

(define_insn "trunchiqi2"
  [(set (match_operand:QI 0 "general_operand" "=g")
	(truncate:QI
	 (match_operand:HI 1 "register_operand" "r")))]
  ""
  "*
{
  if (GET_CODE (operands[0]) == MEM)
    if (CONSTANT_ADDRESS_P (XEXP (operands[0], 0)))
      {
	if (! ((cc_prev_status.flags & CC_KNOW_HI_R31)
	       && (cc_prev_status.flags & CC_HI_R31_ADJ)
	       && XEXP (operands[0], 0) == cc_prev_status.mdep))
	  {
	    CC_STATUS_INIT;
	    cc_status.flags |= CC_KNOW_HI_R31 | CC_HI_R31_ADJ;
	    cc_status.mdep = XEXP (operands[0], 0);
	    output_asm_insn (\"orh %h0,%?r0,%?r31\", operands);
	  }
	return \"st.b %1,%L0(%?r31)\";
      }
    else
      return \"st.b %1,%0\";
  return \"shl %?r0,%1,%0\";
}")

(define_insn "truncsihi2"
  [(set (match_operand:HI 0 "general_operand" "=g")
	(truncate:HI
	 (match_operand:SI 1 "register_operand" "r")))]
  ""
  "*
{
  if (GET_CODE (operands[0]) == MEM)
    if (CONSTANT_ADDRESS_P (XEXP (operands[0], 0)))
      {
	if (! ((cc_prev_status.flags & CC_KNOW_HI_R31)
	       && (cc_prev_status.flags & CC_HI_R31_ADJ)
	       && XEXP (operands[0], 0) == cc_prev_status.mdep))
	  {
	    CC_STATUS_INIT;
	    cc_status.flags |= CC_KNOW_HI_R31 | CC_HI_R31_ADJ;
	    cc_status.mdep = XEXP (operands[0], 0);
	    output_asm_insn (\"orh %h0,%?r0,%?r31\", operands);
	  }
	return \"st.s %1,%L0(%?r31)\";
      }
    else
      return \"st.s %1,%0\";
  return \"shl %?r0,%1,%0\";
}")

;;- zero extension instructions

(define_insn "zero_extendhisi2"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(zero_extend:SI
	 (match_operand:HI 1 "register_operand" "r")))]
  ""
  "*
{
  CC_STATUS_PARTIAL_INIT;
  return \"and 0xffff,%1,%0\";
}")

(define_insn "zero_extendqihi2"
  [(set (match_operand:HI 0 "register_operand" "=r")
	(zero_extend:HI
	 (match_operand:QI 1 "register_operand" "r")))]
  ""
  "*
{
  CC_STATUS_PARTIAL_INIT;
  return \"and 0xff,%1,%0\";
}")

(define_insn "zero_extendqisi2"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(zero_extend:SI
	 (match_operand:QI 1 "register_operand" "r")))]
  ""
  "*
{
  CC_STATUS_PARTIAL_INIT;
  return \"and 0xff,%1,%0\";
}")

;; Sign extension instructions.

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r")
	(sign_extend:SI
	 (match_operand:HI 1 "indexed_operand" "m")))]
  ""
  "ld.s %1,%0")

(define_insn ""
  [(set (match_operand:HI 0 "register_operand" "=r")
	(sign_extend:HI
	 (match_operand:QI 1 "indexed_operand" "m")))]
  ""
  "ld.b %1,%0")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r")
	(sign_extend:SI
	 (match_operand:QI 1 "indexed_operand" "m")))]
  ""
  "ld.b %1,%0")

(define_insn "extendhisi2"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(sign_extend:SI
	 (match_operand:HI 1 "nonimmediate_operand" "mr")))]
  ""
  "*
{
  if (REG_P (operands[1]))
    return \"shl 16,%1,%0\;shra 16,%0,%0\";
  if (GET_CODE (operands[1]) == CONST_INT)
    abort ();
  if (CONSTANT_ADDRESS_P (XEXP (operands[1], 0)))
    {
      CC_STATUS_INIT;
      cc_status.flags |= CC_KNOW_HI_R31 | CC_HI_R31_ADJ;
      cc_status.mdep = XEXP (operands[1], 0);
      return \"orh %h1,%?r0,%?r31\;ld.s %L1(%?r31),%0\";
    }
  else
    return \"ld.s %1,%0\";
}")

(define_insn "extendqihi2"
  [(set (match_operand:HI 0 "register_operand" "=r")
	(sign_extend:HI
	 (match_operand:QI 1 "nonimmediate_operand" "mr")))]
  ""
  "*
{
  if (REG_P (operands[1]))
    return \"shl 24,%1,%0\;shra 24,%0,%0\";
  if (GET_CODE (operands[1]) == CONST_INT)
    abort ();
  if (CONSTANT_ADDRESS_P (XEXP (operands[1], 0)))
    {
      CC_STATUS_INIT;
      cc_status.flags |= CC_KNOW_HI_R31 | CC_HI_R31_ADJ;
      cc_status.mdep = XEXP (operands[1], 0);
      return \"orh %h1,%?r0,%?r31\;ld.b %L1(%?r31),%0\";
    }
  else
    return \"ld.b %1,%0\";
}")

(define_insn "extendqisi2"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(sign_extend:SI
	 (match_operand:QI 1 "nonimmediate_operand" "mr")))]
  ""
  "*
{
  if (REG_P (operands[1]))
    return \"shl 24,%1,%0\;shra 24,%0,%0\";
  if (GET_CODE (operands[1]) == CONST_INT)
    abort ();
  if (CONSTANT_ADDRESS_P (XEXP (operands[1], 0)))
    {
      CC_STATUS_INIT;
      cc_status.flags |= CC_KNOW_HI_R31 | CC_HI_R31_ADJ;
      cc_status.mdep = XEXP (operands[1], 0);
      return \"orh %h1,%?r0,%?r31\;ld.b %L1(%?r31),%0\";
    }
  else
    return \"ld.b %1,%0\";
}")

;; Signed bitfield extractions come out looking like
;;	(shiftrt (sign_extend (shift <Y> <C1>)) <C2>)
;; which we expand poorly as four shift insns.
;; These patters yeild two shifts:
;;	(shiftrt (shift <Y> <C3>) <C4>)
(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r")
	(ashiftrt:SI
	 (sign_extend:SI
	  (match_operand:QI 1 "register_operand" "r"))
	 (match_operand:SI 2 "logic_int" "n")))]
  "INTVAL (operands[2]) < 8"
  "*
{
  return \"shl 24,%1,%0\;shra 24+%2,%0,%0\";
}")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r")
	(ashiftrt:SI
	 (sign_extend:SI
	  (subreg:QI (ashift:SI (match_operand:SI 1 "register_operand" "r")
				(match_operand:SI 2 "logic_int" "n")) 0))
	 (match_operand:SI 3 "logic_int" "n")))]
  "INTVAL (operands[3]) < 8"
  "*
{
  return \"shl 0x18+%2,%1,%0\;shra 0x18+%3,%0,%0\";
}")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r")
	(ashiftrt:SI
	 (sign_extend:SI
	  (ashift:QI (match_operand:QI 1 "register_operand" "r")
		     (match_operand:QI 2 "logic_int" "n")))
	 (match_operand:SI 3 "logic_int" "n")))]
  "INTVAL (operands[3]) < 8"
  "*
{
  return \"shl 0x18+%2,%1,%0\;shra 0x18+%3,%0,%0\";
}")

;; Special patterns for optimizing bit-field instructions.

;; First two patterns are for bitfields that came from memory
;; testing only the high bit.  They work with old combiner.

(define_insn ""
  [(set (cc0)
	(eq (zero_extend:SI (subreg:QI (lshiftrt:SI (match_operand:SI 0 "register_operand" "r")
						    (const_int 7)) 0))
	    (const_int 0)))]
  ""
  "*
{
  CC_STATUS_PARTIAL_INIT;
  return \"and 128,%0,%?r0\";
}")

(define_insn ""
  [(set (cc0)
	(eq (sign_extend:SI (subreg:QI (ashiftrt:SI (match_operand:SI 0 "register_operand" "r")
						    (const_int 7)) 0))
	    (const_int 0)))]
  ""
  "*
{
  CC_STATUS_PARTIAL_INIT;
  return \"and 128,%0,%?r0\";
}")

;; next two patterns are good for bitfields coming from memory
;; (via pseudo-register) or from a register, though this optimization
;; is only good for values contained wholly within the bottom 13 bits
(define_insn ""
  [(set (cc0)
	(eq 
	 (and:SI (lshiftrt:SI (match_operand:SI 0 "register_operand" "r")
			      (match_operand:SI 1 "logic_int" "n"))
		 (match_operand:SI 2 "logic_int" "n"))
	 (const_int 0)))]
  "LOGIC_INTVAL (INTVAL (operands[2]) << INTVAL (operands[1]))"
  "*
{
  CC_STATUS_PARTIAL_INIT;
  operands[2] = gen_rtx (CONST_INT, VOIDmode,
			 (INTVAL (operands[2]) << INTVAL (operands[1])));
  return \"and %2,%0,%?r0\";
}")

(define_insn ""
  [(set (cc0)
	(eq 
	 (and:SI (ashiftrt:SI (match_operand:SI 0 "register_operand" "r")
			      (match_operand:SI 1 "logic_int" "n"))
		 (match_operand:SI 2 "logic_int" "n"))
	 (const_int 0)))]
  "LOGIC_INTVAL (INTVAL (operands[2]) << INTVAL (operands[1]))"
  "*
{
  CC_STATUS_PARTIAL_INIT;
  operands[2] = gen_rtx (CONST_INT, VOIDmode,
			 (INTVAL (operands[2]) << INTVAL (operands[1])));
  return \"and %2,%0,%?r0\";
}")

;; Conversions between float and double.

(define_insn "extendsfdf2"
  [(set (match_operand:DF 0 "register_operand" "=f")
	(float_extend:DF
	 (match_operand:SF 1 "register_operand" "f")))]
  ""
  "fmov.sd %1,%0")

(define_insn "truncdfsf2"
  [(set (match_operand:SF 0 "register_operand" "=f")
	(float_truncate:SF
	 (match_operand:DF 1 "register_operand" "f")))]
  ""
  "fmov.ds %1,%0")

;; Conversion between fixed point and floating point.
;; Note that among the fix-to-float insns
;; the ones that start with SImode come first.
;; That is so that an operand that is a CONST_INT
;; (and therefore lacks a specific machine mode).
;; will be recognized as SImode (which is always valid)
;; rather than as QImode or HImode.

;; This pattern forces (set (reg:SF ...) (float:SF (const_int ...)))
;; to be reloaded by putting the constant into memory.
;; It must come before the more general floatsisf2 pattern.
(define_expand "floatsidf2"
  [(set (match_dup 2) (match_dup 3))
   (set (match_dup 4) (xor:SI (match_operand:SI 1 "register_operand" "")
			      (const_int -2147483648)))
   (set (match_dup 5) (match_dup 3))
   (set (subreg:SI (match_dup 5) 0) (match_dup 4))
   (set (match_operand:DF 0 "register_operand" "")
	(minus:DF (match_dup 5) (match_dup 2)))]
  ""
  "
{
  REAL_VALUE_TYPE d;
  /* 4503601774854144 is  (1 << 30) * ((1 << 22) + (1 << 1)).  */
  d = REAL_VALUE_ATOF (\"4503601774854144\", DFmode);
  operands[2] = gen_reg_rtx (DFmode);
  operands[3] = CONST_DOUBLE_FROM_REAL_VALUE (d, DFmode);
  operands[4] = gen_reg_rtx (SImode);
  operands[5] = gen_reg_rtx (DFmode);
}")

;; Floating to fixed conversion.

(define_expand "fix_truncdfsi2"
  ;; This first insn produces a double-word value
  ;; in which only the low word is valid.
  [(set (match_dup 2)
	(fix:DI (fix:DF (match_operand:DF 1 "register_operand" "f"))))
   (set (match_operand:SI 0 "register_operand" "=f")
	(subreg:SI (match_dup 2) 0))]
  ""
  "
{
  operands[2] = gen_reg_rtx (DImode);
}")

;; Recognize the first insn generated above.
;; This RTL looks like a fix_truncdfdi2 insn,
;; but we dont call it that, because only 32 bits
;; of the result are valid.
;; This pattern will work for the intended purposes 
;; as long as we do not have any fixdfdi2 or fix_truncdfdi2.
(define_insn ""
  [(set (match_operand:DI 0 "register_operand" "=f")
	(fix:DI (fix:DF (match_operand:DF 1 "register_operand" "f"))))]
  ""
  "ftrunc.dd %1,%0")

(define_expand "fix_truncsfsi2"
  ;; This first insn produces a double-word value
  ;; in which only the low word is valid.
  [(set (match_dup 2)
	(fix:DI (fix:SF (match_operand:SF 1 "register_operand" "f"))))
   (set (match_operand:SI 0 "register_operand" "=f")
	(subreg:SI (match_dup 2) 0))]
  ""
  "
{
  operands[2] = gen_reg_rtx (DImode);
}")

;; Recognize the first insn generated above.
;; This RTL looks like a fix_truncsfdi2 insn,
;; but we dont call it that, because only 32 bits
;; of the result are valid.
;; This pattern will work for the intended purposes 
;; as long as we do not have any fixsfdi2 or fix_truncsfdi2.
(define_insn ""
  [(set (match_operand:DI 0 "register_operand" "=f")
	(fix:DI (fix:SF (match_operand:SF 1 "register_operand" "f"))))]
  ""
  "ftrunc.sd %1,%0")

;;- arithmetic instructions

(define_insn "addsi3"
  [(set (match_operand:SI 0 "register_operand" "=r,*f")
	(plus:SI (match_operand:SI 1 "nonmemory_operand" "%r,*f")
		 (match_operand:SI 2 "arith_operand" "rI,*f")))]
  ""
  "*
{
  if (which_alternative == 1)
    return \"fiadd.ss %2,%1,%0\";
  CC_STATUS_PARTIAL_INIT;
  return \"addu %2,%1,%0\";
}")

(define_insn "adddi3"
  [(set (match_operand:DI 0 "register_operand" "=f")
	(plus:DI (match_operand:DI 1 "register_operand" "%f")
		 (match_operand:DI 2 "register_operand" "f")))]
  ""
  "fiadd.dd %1,%2,%0")

(define_insn "subsi3"
  [(set (match_operand:SI 0 "register_operand" "=r,r,*f")
	(minus:SI (match_operand:SI 1 "register_operand" "r,I,*f")
		  (match_operand:SI 2 "arith_operand" "rI,r,*f")))]
  ""
  "*
{
  if (which_alternative == 2)
    return \"fisub.ss %1,%2,%0\";
  CC_STATUS_PARTIAL_INIT;
  if (REG_P (operands[2]))
    return \"subu %1,%2,%0\";
  operands[2] = gen_rtx (CONST_INT, VOIDmode, - INTVAL (operands[2]));
  return \"addu %2,%1,%0\";
}")

(define_insn "subdi3"
  [(set (match_operand:DI 0 "register_operand" "=f")
	(minus:DI (match_operand:DI 1 "register_operand" "%f")
		  (match_operand:DI 2 "register_operand" "f")))]
  ""
  "fisub.dd %1,%2,%0")

(define_expand "mulsi3"
  [(set (subreg:SI (match_dup 4) 0) (match_operand:SI 1 "general_operand" ""))
   (set (subreg:SI (match_dup 5) 0) (match_operand:SI 2 "general_operand" ""))
   (clobber (match_dup 3))
   (set (subreg:SI (match_dup 3) 0)
	(mult:SI (subreg:SI (match_dup 4) 0) (subreg:SI (match_dup 5) 0)))
   (set (match_operand:SI 0 "register_operand" "") (subreg:SI (match_dup 3) 0))]
  ""
  "
{
  if (WORDS_BIG_ENDIAN)
    emit_insn (gen_mulsi3_big (operands[0], operands[1], operands[2]));
  else
    emit_insn (gen_mulsi3_little (operands[0], operands[1], operands[2]));
  DONE;
}")

(define_expand "mulsi3_little"
  [(set (subreg:SI (match_dup 4) 0) (match_operand:SI 1 "general_operand" ""))
   (set (subreg:SI (match_dup 5) 0) (match_operand:SI 2 "general_operand" ""))
   (clobber (match_dup 3))
   (set (subreg:SI (match_dup 3) 0)
	(mult:SI (subreg:SI (match_dup 4) 0) (subreg:SI (match_dup 5) 0)))
   (set (match_operand:SI 0 "register_operand" "") (subreg:SI (match_dup 3) 0))]
  "! WORDS_BIG_ENDIAN"
  "
{
  operands[3] = gen_reg_rtx (DImode);
  operands[4] = gen_reg_rtx (DImode);
  operands[5] = gen_reg_rtx (DImode);
}")

(define_expand "mulsi3_big"
  [(set (subreg:SI (match_dup 4) 1) (match_operand:SI 1 "general_operand" ""))
   (set (subreg:SI (match_dup 5) 1) (match_operand:SI 2 "general_operand" ""))
   (clobber (match_dup 3))
   (set (subreg:SI (match_dup 3) 1)
	(mult:SI (subreg:SI (match_dup 4) 1) (subreg:SI (match_dup 5) 1)))
   (set (match_operand:SI 0 "register_operand" "") (subreg:SI (match_dup 3) 1))]
  "WORDS_BIG_ENDIAN"
  "
{
  operands[3] = gen_reg_rtx (DImode);
  operands[4] = gen_reg_rtx (DImode);
  operands[5] = gen_reg_rtx (DImode);
}")

(define_insn ""
  [(set (subreg:SI (match_operand:DI 0 "register_operand" "=f") 0)
	(mult:SI (subreg:SI (match_operand:DI 1 "register_operand" "f") 0)
		 (subreg:SI (match_operand:DI 2 "register_operand" "f") 0)))]
  "! WORDS_BIG_ENDIAN"
  "fmlow.dd %2,%1,%0")

(define_insn ""
  [(set (subreg:SI (match_operand:DI 0 "register_operand" "=f") 1)
	(mult:SI (subreg:SI (match_operand:DI 1 "register_operand" "f") 1)
		 (subreg:SI (match_operand:DI 2 "register_operand" "f") 1)))]
  "WORDS_BIG_ENDIAN"
  "fmlow.dd %2,%1,%0")

;;- and instructions (with compliment also)			   
(define_insn "andsi3"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(and:SI (match_operand:SI 1 "nonmemory_operand" "%r")
		(match_operand:SI 2 "nonmemory_operand" "rL")))]
  ""
  "*
{
  rtx xop[3];

  CC_STATUS_PARTIAL_INIT;
  if (REG_P (operands[2]) || LOGIC_INT (operands[2]))
    return \"and %2,%1,%0\";
  if ((INTVAL (operands[2]) & 0xffff) == 0)
    {
      operands[2] = gen_rtx (CONST_INT, VOIDmode, 
			     (unsigned) INTVAL (operands[2]) >> 16);
      return \"andh %2,%1,%0\";
    }
  xop[0] = operands[0];
  xop[1] = operands[1];
  xop[2] = gen_rtx (CONST_INT, VOIDmode, ~INTVAL (operands[2]) & 0xffff);
  output_asm_insn (\"andnot %2,%1,%0\", xop);
  operands[2] = gen_rtx (CONST_INT, VOIDmode, 
			 ~(unsigned) INTVAL (operands[2]) >> 16);
  return \"andnoth %2,%0,%0\";
}")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r")
	(and:SI (not:SI (match_operand:SI 1 "register_operand" "rn"))
		(match_operand:SI 2 "register_operand" "r")))]
  ""
  "*
{
  rtx xop[3];

  CC_STATUS_PARTIAL_INIT;
  if (REG_P (operands[1]) || LOGIC_INT (operands[1]))
    return \"andnot %1,%2,%0\";
  if ((INTVAL (operands[1]) & 0xffff) == 0)
    {
      operands[1] = gen_rtx (CONST_INT, VOIDmode, 
			     (unsigned) INTVAL (operands[1]) >> 16);
      return \"andnoth %1,%2,%0\";
    }
  xop[0] = operands[0];
  xop[1] = gen_rtx (CONST_INT, VOIDmode, (INTVAL (operands[1]) & 0xffff));
  xop[2] = operands[2];
  output_asm_insn (\"andnot %1,%2,%0\", xop);
  operands[1] = gen_rtx (CONST_INT, VOIDmode, 
			 (unsigned) INTVAL (operands[1]) >> 16);
  return \"andnoth %1,%0,%0\";
}")

(define_insn "iorsi3"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(ior:SI (match_operand:SI 1 "nonmemory_operand" "%r")
		(match_operand:SI 2 "nonmemory_operand" "rL")))]
  ""
  "*
{
  rtx xop[3];

  CC_STATUS_PARTIAL_INIT;
  if (REG_P (operands[2]) || LOGIC_INT (operands[2]))
    return \"or %2,%1,%0\";
  if ((INTVAL (operands[2]) & 0xffff) == 0)
    {
      operands[2] = gen_rtx (CONST_INT, VOIDmode, 
			     (unsigned) INTVAL (operands[2]) >> 16);
      return \"orh %2,%1,%0\";
    }
  xop[0] = operands[0];
  xop[1] = operands[1];
  xop[2] = gen_rtx (CONST_INT, VOIDmode, (INTVAL (operands[2]) & 0xffff));
  output_asm_insn (\"or %2,%1,%0\", xop);
  operands[2] = gen_rtx (CONST_INT, VOIDmode, 
			 (unsigned) INTVAL (operands[2]) >> 16);
  return \"orh %2,%0,%0\";
}")

(define_insn "xorsi3"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(xor:SI (match_operand:SI 1 "nonmemory_operand" "%r")
		(match_operand:SI 2 "nonmemory_operand" "rL")))]
  ""
  "*
{
  rtx xop[3];

  CC_STATUS_PARTIAL_INIT;
  if (REG_P (operands[2]) || LOGIC_INT (operands[2]))
    return \"xor %2,%1,%0\";
  if ((INTVAL (operands[2]) & 0xffff) == 0)
    {
      operands[2] = gen_rtx (CONST_INT, VOIDmode, 
			     (unsigned) INTVAL (operands[2]) >> 16);
      return \"xorh %2,%1,%0\";
    }
  xop[0] = operands[0];
  xop[1] = operands[1];
  xop[2] = gen_rtx (CONST_INT, VOIDmode, (INTVAL (operands[2]) & 0xffff));
  output_asm_insn (\"xor %2,%1,%0\", xop);
  operands[2] = gen_rtx (CONST_INT, VOIDmode, 
			 (unsigned) INTVAL (operands[2]) >> 16);
  return \"xorh %2,%0,%0\";
}")

;(The i860 instruction set doesn't allow an immediate second operand in
; a subtraction.)
(define_insn "negsi2"
  [(set (match_operand:SI 0 "general_operand" "=r")
	(neg:SI (match_operand:SI 1 "arith_operand" "r")))]
  ""
  "*
{
  CC_STATUS_PARTIAL_INIT;
  return \"subu %?r0,%1,%0\";
}")

(define_insn "one_cmplsi2"
  [(set (match_operand:SI 0 "general_operand" "=r")
	(not:SI (match_operand:SI 1 "arith_operand" "r")))]
  ""
  "*
{
  CC_STATUS_PARTIAL_INIT;
  return \"subu -1,%1,%0\";
}")

;; Floating point arithmetic instructions.

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

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

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

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

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

(define_insn "mulsf3"
  [(set (match_operand:SF 0 "register_operand" "=f")
	(mult:SF (match_operand:SF 1 "register_operand" "f")
		 (match_operand:SF 2 "register_operand" "f")))]
  ""
  "fmul.ss %1,%2,%0")

(define_insn "negdf2"
  [(set (match_operand:DF 0 "register_operand" "=f")
	(neg:DF (match_operand:DF 1 "register_operand" "f")))]
  ""
  "fsub.dd %?f0,%1,%0")

(define_insn "negsf2"
  [(set (match_operand:SF 0 "register_operand" "=f")
	(neg:SF (match_operand:SF 1 "register_operand" "f")))]
  ""
  "fsub.ss %?f0,%1,%0")

(define_insn "divdf3"
  [(set (match_operand:DF 0 "register_operand" "=&f")
	(div:DF (match_operand:DF 1 "register_operand" "f")
		 (match_operand:DF 2 "register_operand" "f")))
   (clobber (match_scratch:DF 3 "=&f"))
   (clobber (match_scratch:DF 4 "=&f"))]
  ""
  "*
{
  CC_STATUS_PARTIAL_INIT;
  if (((cc_prev_status.flags & CC_KNOW_HI_R31) == 0)
      || (cc_prev_status.flags & CC_HI_R31_ADJ)
      || (cc_prev_status.mdep != CONST2_RTX (SFmode)))
    {
      cc_status.flags |= CC_KNOW_HI_R31;
      cc_status.flags &= ~CC_HI_R31_ADJ;
      cc_status.mdep = CONST2_RTX (SFmode); 
      return \"frcp.dd %2,%3\;fmul.dd %2,%3,%0\;fmov.dd %?f0,%4\;\\\
orh 0x4000,%?r0,%?r31\;ixfr %?r31,%R4\;fsub.dd %4,%0,%0\;\\\
fmul.dd %3,%0,%3\;fmul.dd %2,%3,%0\;fsub.dd %4,%0,%0\;\\\
fmul.dd %3,%0,%3\;fmul.dd %2,%3,%0\;fsub.dd %4,%0,%0\;\\\
fmul.dd %3,%1,%3\;fmul.dd %0,%3,%0\";
    }
  else
    return \"frcp.dd %2,%3\;fmul.dd %2,%3,%0\;fmov.dd %?f0,%4\;\\\
ixfr %?r31,%R4\;fsub.dd %4,%0,%0\;\\\
fmul.dd %3,%0,%3\;fmul.dd %2,%3,%0\;fsub.dd %4,%0,%0\;\\\
fmul.dd %3,%0,%3\;fmul.dd %2,%3,%0\;fsub.dd %4,%0,%0\;\\\
fmul.dd %3,%1,%3\;fmul.dd %0,%3,%0\";
}")

(define_insn "divsf3"
  [(set (match_operand:SF 0 "register_operand" "=&f")
	(div:SF (match_operand:SF 1 "register_operand" "f")
		 (match_operand:SF 2 "register_operand" "f")))
   (clobber (match_scratch:SF 3 "=&f"))
   (clobber (match_scratch:SF 4 "=&f"))]
  ""
  "*
{
  CC_STATUS_PARTIAL_INIT;
  if (((cc_prev_status.flags & CC_KNOW_HI_R31) == 0)
      || (cc_prev_status.flags & CC_HI_R31_ADJ)
      || (cc_prev_status.mdep != CONST2_RTX (SFmode)))
    {
      cc_status.flags |= CC_KNOW_HI_R31;
      cc_status.flags &= ~CC_HI_R31_ADJ;
      cc_status.mdep = CONST2_RTX (SFmode);
      output_asm_insn (\"orh 0x4000,%?r0,%?r31\", operands);
    }
  return \"ixfr %?r31,%4\;frcp.ss %2,%0\;\\\
fmul.ss %2,%0,%3\;fsub.ss %4,%3,%3\;fmul.ss %0,%3,%0\;\\\
fmul.ss %2,%0,%3\;fsub.ss %4,%3,%3\;\\\
fmul.ss %1,%0,%4\;fmul.ss %3,%4,%0\";
}")

;; Shift instructions

;; Optimized special case 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)))]
  ""
  "*
{
  if (CONSTANT_ADDRESS_P (XEXP (operands[1], 0)))
    {
      CC_STATUS_INIT;
      cc_status.flags |= CC_KNOW_HI_R31 | CC_HI_R31_ADJ;
      cc_status.mdep = XEXP (operands[1], 0);
      return \"orh %h1,%?r0,%?r31\;ld.b %L1(%?r31),%0\";
    }
  return \"ld.b %1,%0\";
}")


;;- 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 "shift_operand" "rn")))]
  ""
  "*
{
  return \"shl %2,%1,%0\";
}")

(define_insn "ashlhi3"
  [(set (match_operand:HI 0 "register_operand" "=r")
	(ashift:HI (match_operand:HI 1 "register_operand" "r")
		   (match_operand:HI 2 "shift_operand" "rn")))]
  ""
  "*
{
  return \"shl %2,%1,%0\";
}")

(define_insn "ashlqi3"
  [(set (match_operand:QI 0 "register_operand" "=r")
	(ashift:QI (match_operand:QI 1 "register_operand" "r")
		   (match_operand:QI 2 "shift_operand" "rn")))]
  ""
  "*
{
  return \"shl %2,%1,%0\";
}")

(define_insn "ashrsi3"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(ashiftrt:SI (match_operand:SI 1 "register_operand" "r")
		     (match_operand:SI 2 "shift_operand" "rn")))]
  ""
  "*
{
  return \"shra %2,%1,%0\";
}")

(define_insn "lshrsi3"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(lshiftrt:SI (match_operand:SI 1 "register_operand" "r")
		     (match_operand:SI 2 "shift_operand" "rn")))]
  ""
  "*
{
  return \"shr %2,%1,%0\";
}")

;; Unconditional and other jump instructions

(define_insn "jump"
  [(set (pc) (label_ref (match_operand 0 "" "")))]
  ""
  "*
{
  return \"br %l0\;nop\";
}")

;; Here are two simple peepholes which fill the delay slot of
;; an unconditional branch.

(define_peephole
  [(set (match_operand:SI 0 "register_operand" "=rf")
	(match_operand:SI 1 "single_insn_src_p" "gfG"))
   (set (pc) (label_ref (match_operand 2 "" "")))]
  ""
  "* return output_delayed_branch (\"br %l2\", operands, insn);")

(define_peephole
  [(set (match_operand:SI 0 "memory_operand" "=m")
	(match_operand:SI 1 "reg_or_0_operand" "rfJ"))
   (set (pc) (label_ref (match_operand 2 "" "")))]
  ""
  "* return output_delayed_branch (\"br %l2\", operands, insn);")

(define_insn "tablejump"
  [(set (pc) (match_operand:SI 0 "register_operand" "r"))
   (use (label_ref (match_operand 1 "" "")))]
  ""
  "bri %0\;nop")

(define_peephole
  [(set (match_operand:SI 0 "memory_operand" "=m")
	(match_operand:SI 1 "reg_or_0_operand" "rfJ"))
   (set (pc) (match_operand:SI 2 "register_operand" "r"))
   (use (label_ref (match_operand 3 "" "")))]
  ""
  "* return output_delayed_branch (\"bri %2\", operands, insn);")

;;- jump to subroutine
(define_expand "call"
  [(call (match_operand:SI 0 "memory_operand" "m")
	 (match_operand 1 "" "i"))]
  ;; operand[2] is next_arg_register
  ""
  "
{
  /* Make sure the address is just one reg and will stay that way.  */
  if (! call_insn_operand (operands[0], QImode))
    operands[0]
      = change_address (operands[0], VOIDmode,
			copy_to_mode_reg (Pmode, XEXP (operands[0], 0)));
  if (INTVAL (operands[1]) > 0)
    {
      emit_move_insn (arg_pointer_rtx, stack_pointer_rtx);
      emit_insn (gen_rtx (USE, VOIDmode, arg_pointer_rtx));
    }
}")

;;- jump to subroutine
(define_insn ""
  [(call (match_operand:SI 0 "call_insn_operand" "m")
	 (match_operand 1 "" "i"))]
  ;; operand[2] is next_arg_register
  ""
  "*
{
  /* strip the MEM.  */
  operands[0] = XEXP (operands[0], 0);
  CC_STATUS_INIT;
  if (GET_CODE (operands[0]) == REG)
    return \"calli %0\;nop\";
  return \"call %0\;nop\";
}")

(define_peephole
  [(set (match_operand:SI 0 "register_operand" "=rf")
	(match_operand:SI 1 "single_insn_src_p" "gfG"))
   (call (match_operand:SI 2 "memory_operand" "m")
	 (match_operand 3 "" "i"))]
  ;;- Don't use operand 1 for most machines.
  "! reg_mentioned_p (operands[0], operands[2])"
  "*
{
  /* strip the MEM.  */
  operands[2] = XEXP (operands[2], 0);
  if (GET_CODE (operands[2]) == REG)
    return output_delayed_branch (\"calli %2\", operands, insn);
  return output_delayed_branch (\"call %2\", operands, insn);
}")

(define_peephole
  [(set (match_operand:SI 0 "memory_operand" "=m")
	(match_operand:SI 1 "reg_or_0_operand" "rfJ"))
   (call (match_operand:SI 2 "call_insn_operand" "m")
	 (match_operand 3 "" "i"))]
  ;;- Don't use operand 1 for most machines.
  ""
  "*
{
  /* strip the MEM.  */
  operands[2] = XEXP (operands[2], 0);
  if (GET_CODE (operands[2]) == REG)
    return output_delayed_branch (\"calli %2\", operands, insn);
  return output_delayed_branch (\"call %2\", operands, insn);
}")

(define_expand "call_value"
  [(set (match_operand 0 "register_operand" "=rf")
	(call (match_operand:SI 1 "memory_operand" "m")
	      (match_operand 2 "" "i")))]
  ;; operand 3 is next_arg_register
  ""
  "
{
  /* Make sure the address is just one reg and will stay that way.  */
  if (! call_insn_operand (operands[1], QImode))
    operands[1]
      = change_address (operands[1], VOIDmode,
			copy_to_mode_reg (Pmode, XEXP (operands[1], 0)));
  if (INTVAL (operands[2]) > 0)
    {
      emit_move_insn (arg_pointer_rtx, stack_pointer_rtx);
      emit_insn (gen_rtx (USE, VOIDmode, arg_pointer_rtx));
    }
}")

(define_insn ""
  [(set (match_operand 0 "register_operand" "=rf")
	(call (match_operand:SI 1 "call_insn_operand" "m")
	      (match_operand 2 "" "i")))]
  ;; operand 3 is next_arg_register
  ""
  "*
{
  /* strip the MEM.  */
  operands[1] = XEXP (operands[1], 0);
  CC_STATUS_INIT;
  if (GET_CODE (operands[1]) == REG)
    return \"calli %1\;nop\";
  return \"call %1\;nop\";
}")

(define_peephole
  [(set (match_operand:SI 0 "register_operand" "=rf")
	(match_operand:SI 1 "single_insn_src_p" "gfG"))
   (set (match_operand 2 "" "=rf")
	(call (match_operand:SI 3 "call_insn_operand" "m")
	      (match_operand 4 "" "i")))]
  ;;- Don't use operand 4 for most machines.
  "! reg_mentioned_p (operands[0], operands[3])"
  "*
{
  /* strip the MEM.  */
  operands[3] = XEXP (operands[3], 0);
  if (GET_CODE (operands[3]) == REG)
    return output_delayed_branch (\"calli %3\", operands, insn);
  return output_delayed_branch (\"call %3\", operands, insn);
}")

(define_peephole
  [(set (match_operand:SI 0 "memory_operand" "=m")
	(match_operand:SI 1 "reg_or_0_operand" "rJf"))
   (set (match_operand 2 "" "=rf")
	(call (match_operand:SI 3 "call_insn_operand" "m")
	      (match_operand 4 "" "i")))]
  ;;- Don't use operand 4 for most machines.
  ""
  "*
{
  /* strip the MEM.  */
  operands[3] = XEXP (operands[3], 0);
  if (GET_CODE (operands[3]) == REG)
    return output_delayed_branch (\"calli %3\", operands, insn);
  return output_delayed_branch (\"call %3\", operands, insn);
}")

;; Call subroutine returning any type.

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

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

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

  /* The optimizer does not know that the call sets the function value
     registers we stored in the result block.  We avoid problems by
     claiming that all hard registers are used and clobbered at this
     point.  */
  emit_insn (gen_blockage ());

  DONE;
}")

;; UNSPEC_VOLATILE is considered to use and clobber all hard registers and
;; all of memory.  This blocks insns from being moved across this point.

(define_insn "blockage"
  [(unspec_volatile [(const_int 0)] 0)]
  ""
  "")

(define_insn "nop"
  [(const_int 0)]
  ""
  "nop")

(define_insn "indirect_jump"
  [(set (pc) (match_operand:SI 0 "register_operand" "r"))]
  ""
  "bri %0")

;;
;; A special insn that does the work to get setup just
;; before a table jump.
;;
(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r")
	(mem:SI (plus:SI (match_operand:SI 1 "register_operand" "r")
			 (label_ref (match_operand 2 "" "")))))]
  ""
  "*
{
  CC_STATUS_INIT;
  return \"orh %H2,%?r0,%?r31\;or %L2,%?r31,%?r31\;ld.l %?r31(%1),%0\";
}")
  
(define_peephole
  [(set (match_operand:SI 0 "register_operand" "=rf")
	(match_operand:SI 1 "single_insn_src_p" "gfG"))
   (set (pc) (match_operand:SI 2 "register_operand" "r"))
   (use (label_ref (match_operand 3 "" "")))]
  "REGNO (operands[0]) != REGNO (operands[2])"
  "* return output_delayed_branch (\"bri %2\", operands, insn);")
