;;  Mips.md        Naive version of Machine Description for MIPS
;;  Contributed by   A. Lichnewsky, lich@inria.inria.fr
;;  Changes by       Michael Meissner, meissner@osf.org
;;  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 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.

;;
;;------------------------------------------------------------------------
;;

;;
;;  ....................
;;
;;  Peephole Optimizations for
;;
;;          ARITHMETIC
;;
;;  ....................
;;
					;;- The following peepholes are
					;;- motivated by the fact that
					;;- stack movement result in some
					;;- cases in embarrassing sequences
					;;- of addiu SP,SP,int
					;;-    addiu SP,SP,other_int

					;;- --------------------
					;;- REMARK: this would be done better
					;;- by analysis of dependencies in
					;;- basic blocks, prior to REG ALLOC,
					;;- and simplification of trees:
					;;-      (+  (+ REG const) const)
					;;- ->   (+ REG newconst)
					;;- --------------------
					;;- Merged peephole code from
					;;- raeburn@ATHENA.MIT.EDU

(define_peephole
  [(set (match_operand:SI 0 "register_operand" "=r")
        (match_operator 1 "additive_op"
                        [(match_operand:SI 2 "register_operand" "r")
                         (match_operand:SI 3 "small_int" "I")]))
   (set (match_operand:SI 4 "register_operand" "=r")
        (match_operator 5 "additive_op"
                        [(match_dup 0)
                         (match_operand:SI 6 "small_int" "I")]))]
  "(REGNO (operands[0]) == REGNO (operands[4])
    || dead_or_set_p (insn, operands[0]))"
  "*
{
  int addend;
  /* compute sum, with signs */
  addend = INTVAL (operands[3]) * (GET_CODE (operands[1]) == PLUS ? 1 : -1);
  addend += INTVAL (operands[6]) * (GET_CODE (operands[5]) == PLUS ? 1 : -1);
  if (addend != 0)
    {
      operands[0] = gen_rtx (CONST_INT, VOIDmode, addend);
      return \"addi%:\\t%4,%2,%0\";
    }
  /* value is zero; copy */
  if (REGNO (operands[4]) != REGNO (operands[2]))
    return \"add%:\\t%4,%2,$0\";
  /* copying to self; punt */
  return \" # null operation: additive operands cancel (%0,%2)\";
}")

;;
;;  ....................
;;
;;          ARITHMETIC
;;
;;  ....................
;;

(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")))]
  ""
  "add.d\\t%0,%1,%2")

(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")))]
  ""
  "add.s\\t%0,%1,%2")

;; The following is generated when omiting the frame pointer
;; and for referencing large auto arrays during optimization.

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r")
	(plus:SI (match_operand:SI 1 "register_operand" "%r")
		 (match_operand:SI 2 "immediate_operand" "i")))]
  "operands[1] == stack_pointer_rtx || operands[1] == frame_pointer_rtx"
  "*
{
  int number;
  if (GET_CODE (operands[2]) != CONST_INT)
    return \"add%:\\t%0,%1,%2\";

  number = INTVAL (operands[2]);
  if (((unsigned) (number + 0x8000) > 0xffff))
    {
      operands[3] = gen_rtx (REG, SImode, 1);	/* assembler temp. */
      return \".set\\tnoat\\n\\tli\\t%3,%2\\n\\tadd%:\\t%0,%1,%3\\n\\t.set\\tat\";
    }

  return (number < 0) ? \"sub%:\\t%0,%1,%n2\" : \"add%:\\t%0,%1,%2\";
}")

(define_insn "addsi3"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(plus:SI (match_operand:SI 1 "arith_operand" "%r")
		 (match_operand:SI 2 "arith_operand" "rI")))]
  ""
  "*
{
  return (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) < 0)
    ? \"sub%:\\t%0,%1,%n2\"
    : \"add%:\\t%0,%1,%2\";
}")


;;- All kinds of subtract instructions.

(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")))]
  ""
  "sub.d\\t%0,%1,%2")

(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")))]
  ""
  "sub.s\\t%0,%1,%2")

;; The following is generated when omiting the frame pointer
;; and for referencing large auto arrays during optimization.

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r")
	(minus:SI (match_operand:SI 1 "register_operand" "%r")
		  (match_operand:SI 2 "immediate_operand" "i")))]
  "operands[1] == stack_pointer_rtx || operands[1] == frame_pointer_rtx"
  "*
{
  int number;
  if (GET_CODE (operands[2]) != CONST_INT)
    return \"sub%:\\t%0,%1,%2\";

  number = INTVAL (operands[2]);
  if (((unsigned) (number + 0x8000) > 0xffff))
    {
      operands[3] = gen_rtx (REG, SImode, 1);	/* assembler temp. */
      return \".set\\tnoat\\n\\tli\\t%3,%2\\n\\tsub%:\\t%0,%1,%3\\n\\t.set\\tat\";
    }

  return (number < 0) ? \"add%:\\t%0,%1,%n2\" : \"sub%:\\t%0,%1,%2\";
}")

(define_insn "subsi3"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(minus:SI (match_operand:SI 1 "register_operand" "r")
		  (match_operand:SI 2 "arith_operand" "rI")))]
  ""
  "*
{
  return (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) < 0)
    ? \"add%:\\t%0,%1,%n2\"
    : \"sub%:\\t%0,%1,%2\";
}")


;;- Multiply instructions.

(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")))]
  ""
  "mul.d\\t%0,%1,%2")

(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")))]
  ""
  "mul.s\\t%0,%1,%2")

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


;;- Divide instructions.

(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")))]
  ""
  "div.d\\t%0,%1,%2")

(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")))]
  ""
  "div.s\\t%0,%1,%2")

(define_insn "divmodsi4"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(div:SI (match_operand:SI 1 "register_operand" "r")
		(match_operand:SI 2 "arith_operand" "rI")))
   (set (match_operand:SI 3 "register_operand" "=r")
	(mod:SI (match_dup 1)
		(match_dup 2)))]
  ""
  "div\\t$0,%1,%2\\n\\tmflo\\t%0\\t\\t#quotient\\n\\tmfhi\\t%3\\t\\t#remainder")

(define_insn "divsi3"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(div:SI (match_operand:SI 1 "register_operand" "r")
		(match_operand:SI 2 "arith_operand" "rI")))]
  ""
  "div\\t%0,%1,%2")

(define_insn "udivmodsi4"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(udiv:SI (match_operand:SI 1 "register_operand" "r")
		 (match_operand:SI 2 "arith_operand" "rI")))
   (set (match_operand:SI 3 "register_operand" "=r")
	(umod:SI (match_dup 1)
		 (match_dup 2)))]
  ""
  "divu\\t$0,%1,%2\\n\\tmflo\\t%0\\t\\t#quotient\\n\\tmfhi\\t%3\\t\\t#remainder")

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


;; Remainder instructions


(define_insn "modsi3"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(mod:SI (match_operand:SI 1 "register_operand" "r")
		(match_operand:SI 2 "arith_operand" "rI")))]
  ""
  "rem\\t%0,%1,%2")

(define_insn "umodsi3"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(umod:SI (match_operand:SI 1 "register_operand" "r")
		 (match_operand:SI 2 "arith_operand" "rI")))]
  ""
  "remu\\t%0,%1,%2")


;; Absoluate value instructions -- Don't use the integer abs,
;; since that signals an exception on -2147483648 (sigh).

(define_insn "abssf2"
  [(set (match_operand:SF 0 "register_operand" "=f")
	(abs:SF (match_operand:SF 1 "register_operand" "f")))]
  ""
  "abs.s\\t%0,%1")

(define_insn "absdf2"
  [(set (match_operand:DF 0 "register_operand" "=f")
	(abs:DF (match_operand:DF 1 "register_operand" "f")))]
  ""
  "abs.d\\t%0,%1")

;;
;;  ....................
;;
;;          LOGICAL
;;
;;  ....................
;;

(define_insn "anddi3"
  [(set (match_operand:DI 0 "register_operand" "=&r")
	(and:DI (match_operand:DI 1 "register_operand" "r")
                (match_operand:DI 2 "register_operand" "r")))]
  ""
  "and\\t%0,%1,%2\;and\\t%D0,%D1,%D2")

(define_insn "andsi3"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(and:SI (match_operand:SI 1 "uns_arith_operand" "%r")
		(match_operand:SI 2 "uns_arith_operand" "rK")))]
  ""
  "*
{
  return (GET_CODE (operands[2]) == CONST_INT)
		? \"andi\\t%0,%1,%x2\"
		: \"and\\t%0,%1,%2\";
}")


;; Simple hack to recognize the "nor" instruction on the MIPS
;; [rms: I don't think the following is actually required.]
;; This must appear before the normal or patterns, so that the
;; combiner will correctly fold things.

(define_insn ""
  [(set (match_operand:DI 0 "register_operand" "=r")
	(not:DI (ior:DI (match_operand:DI 1 "register_operand" "r")
			(match_operand:DI 2 "register_operand" "r"))))]
  ""
  "nor\\t%0,%1,%2\;nor\\t%D0,%D1,%D2")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r")
	(not:SI (ior:SI (match_operand:SI 1 "register_operand" "r")
			(match_operand:SI 2 "register_operand" "r"))))]
  ""
  "nor\\t%0,%1,%2")

(define_insn "iordi3"
  [(set (match_operand:DI 0 "register_operand" "=&r")
	(ior:DI (match_operand:DI 1 "register_operand" "r")
                (match_operand:DI 2 "register_operand" "r")))]
  ""
  "or\\t%0,%1,%2\;or\\t%D0,%D1,%D2")

(define_insn "iorsi3"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(ior:SI (match_operand:SI 1 "uns_arith_operand" "%r")
		(match_operand:SI 2 "uns_arith_operand" "rJ")))]
  ""
  "*
{
  return (GET_CODE (operands[2]) == CONST_INT)
		? \"ori\\t%0,%1,%x2\"
		: \"or\\t%0,%1,%2\";
}")

(define_insn "xordi3"
  [(set (match_operand:DI 0 "register_operand" "=&r")
	(xor:DI (match_operand:DI 1 "register_operand" "r")
                (match_operand:DI 2 "register_operand" "r")))]
  ""
  "xor\\t%0,%1,%2\;xor\\t%D0,%D1,%D2")

(define_insn "xorsi3"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(xor:SI (match_operand:SI 1 "uns_arith_operand" "%r")
		(match_operand:SI 2 "uns_arith_operand" "rK")))]
  ""
  "*
{
  return (GET_CODE (operands[2]) == CONST_INT)
		? \"xori\\t%0,%1,%x2\"
		: \"xor\\t%0,%1,%2\";
}")

;;
;;  ....................
;;
;;          TRUNCATION
;;
;;  ....................

;; Extension and truncation insns.
;; Those for integer source operand
;; are ordered widest source type first.


(define_insn "truncsiqi2"
  [(set (match_operand:QI 0 "register_operand" "=r")
	(truncate:QI (match_operand:SI 1 "register_operand" "r")))]
  ""
  "andi\\t%0,%1,0xff\\t#truncsiqi2\\t %1 -> %0")

(define_insn "truncsihi2"
  [(set (match_operand:HI 0 "register_operand" "=r")
	(truncate:HI (match_operand:SI 1 "register_operand" "r")))]
  ""
  "*
    output_asm_insn (\"sll\\t%0,%1,0x10\\t#truncsihi2\\t %1 -> %0\",
                    operands);
    return \"sra\\t%0,%0,0x10\\t#truncsihi2\\t %1 -> %0\";
")

(define_insn "trunchiqi2"
  [(set (match_operand:QI 0 "register_operand" "=r")
	(truncate:QI (match_operand:HI 1 "register_operand" "r")))]
  ""
  "andi\\t%0,%1,0xff\\t#trunchiqi2\\t %1 -> %0")

(define_insn "truncdfsf2"
  [(set (match_operand:SF 0 "register_operand" "=f")
	(float_truncate:SF (match_operand:DF 1 "register_operand" "f")))]
  ""
  "cvt.s.d\\t%0,%1\\t#truncdfsf2\\t %1 -> %0")

;;
;;  ....................
;;
;;          ZERO EXTENSION
;;
;;  ....................

;; Extension insns.
;; Those for integer source operand
;; are ordered widest source type first.



(define_insn "zero_extendhisi2"
  [(set (match_operand:SI 0 "register_operand" "=r,r")
	(zero_extend:SI (match_operand:HI 1 "general_operand" "r,m")))]
  ""
  "*
{
  if (which_alternative == 0)
    {
      output_asm_insn (\"sll\\t%0,%1,0x10\\t#zero_extendhisi2\\t %1 -> %0\",
		       operands);
      return \"srl\\t%0,%0,0x10\\t#zero_extendhisi2\\t %1 -> %0\";
    }
  else
    return \"lhu\\t%0,%1\\t#zero extendhisi2 %1 -> %0\";
}")

(define_insn "zero_extendqihi2"
  [(set (match_operand:HI 0 "register_operand" "=r")
	(zero_extend:HI (match_operand:QI 1 "register_operand" "r")))]
  ""
  "*
    output_asm_insn (\"sll\\t%0,%1,0x18\\t#zero_extendqihi2\\t %1 -> %0\",
                    operands);
    return \"srl\\t%0,%0,0x18\\t#zero_extendqihi2\\t %1 -> %0\";
  ")


(define_insn "zero_extendqisi2"
  [(set (match_operand:SI 0 "register_operand" "=r,r")
	(zero_extend:SI (match_operand:QI 1 "general_operand" "r,m")))]
  ""
  "*
{
  if (which_alternative == 0)
    {
      return \"andi\\t%0,%1,0xff\\t#zero_extendqisi2\\t %1 -> %0\";
    }
  else
    return \"lbu\\t%0,%1\\t#zero extendqisi2 %1 -> %0\";
}")


;;
;;  ....................
;;
;;          SIGN EXTENSION
;;
;;  ....................

;; Extension insns.
;; Those for integer source operand
;; are ordered widest source type first.



(define_insn "extendhisi2"
  [(set (match_operand:SI 0 "register_operand" "=r,r")
	(sign_extend:SI (match_operand:HI 1 "general_operand" "r,m")))]
  ""
  "*
{
  if (which_alternative == 0)
    {
      output_asm_insn (\"sll\\t%0,%1,0x10\\t#sign extendhisi2\\t %1 -> %0\",
		       operands);
      return \"sra\\t%0,%0,0x10\\t#sign extendhisi2\\t %1 -> %0\";
    }
  else
    return \"lh\\t%0,%1\\t#sign extendhisi2 %1 -> %0\";
}")

(define_insn "extendqihi2"
  [(set (match_operand:HI 0 "register_operand" "=r")
	(sign_extend:HI (match_operand:QI 1 "register_operand" "r")))]
  ""
  "*
    output_asm_insn (\"sll\\t%0,%1,0x18\\t#sign extendqihi2\\t %1 -> %0\",
                    operands);
    return \"sra\\t%0,%0,0x18\\t#sign extendqihi2\\t %1 -> %0\";
  ")


(define_insn "extendqisi2"
  [(set (match_operand:SI 0 "register_operand" "=r,r")
	(sign_extend:SI (match_operand:QI 1 "general_operand" "r,m")))]
  ""
  "*
{
  if (which_alternative == 0)
    {
      output_asm_insn (\"sll\\t%0,%1,0x18\\t#sign extendqisi2\\t %1 -> %0\",
		       operands);
      return \"sra\\t%0,%0,0x18\\t#sign extendqisi2\\t %1 -> %0\";
    }
  else
    return \"lb\\t%0,%1\\t#sign extendqisi2 %1 -> %0\";
}")


(define_insn "extendsfdf2"
  [(set (match_operand:DF 0 "register_operand" "=f")
	(float_extend:DF (match_operand:SF 1 "register_operand" "f")))]
  ""
  "cvt.d.s\\t%0,%1\\t#extendsfdf2\\t %1 -> %0")


;;
;;  ....................
;;
;;          CONVERSIONS
;;
;;  ....................


(define_insn "fix_truncdfsi2_internal"
  [(set (match_operand:DF 0 "register_operand" "=f")
	(fix:DF (match_operand:DF 1 "register_operand" "f")))
   (clobber (match_operand:SI 2 "register_operand" "r"))]
  ""
  "trunc.w.d %0,%1,%2")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r")
	(fix:SI (fix:DF (match_operand:DF 1 "register_operand" "f"))))]
  ""
  "mfc1\\t%0,%1")

(define_expand "fix_truncdfsi2"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(fix:SI (fix:DF (match_operand:DF 1 "register_operand" "f"))))]
  ""
  "
{
  rtx reg1 = gen_reg_rtx (DFmode);	/* fp reg that gets trunc result */
  rtx reg2 = gen_reg_rtx (SImode);	/* gp reg that saves FP status bits */
  emit_insn (gen_fix_truncdfsi2_internal (reg1, operands[1], reg2));
  operands[1] = reg1;
  /* Fall through and generate default code */
}")

(define_insn "fix_truncsfsi2_internal"
  [(set (match_operand:SF 0 "register_operand" "=f")
	(fix:SF (match_operand:SF 1 "register_operand" "f")))
   (clobber (match_operand:SI 2 "register_operand" "r"))]
  ""
  "trunc.w.s %0,%1,%2")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r")
	(fix:SI (fix:SF (match_operand:SF 1 "register_operand" "f"))))]
  ""
  "mfc1\\t%0,%1")

(define_expand "fix_truncsfsi2"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(fix:SI (fix:SF (match_operand:SF 1 "register_operand" "f"))))]
  ""
  "
{
  rtx reg1 = gen_reg_rtx (SFmode);	/* fp reg that gets trunc result */
  rtx reg2 = gen_reg_rtx (SImode);	/* gp reg that saves FP status bits */
  emit_insn (gen_fix_truncsfsi2_internal (reg1, operands[1], reg2));
  operands[1] = reg1;
  /* Fall through and generate default code */
}")

(define_insn "floatsidf2"
  [(set (match_operand:DF 0 "register_operand" "=f")
	(float:DF (match_operand:SI 1 "register_operand" "r")))]
  ""
  "mtc1\\t%1,%0\\t\\t#floatsidf2\\t%1 -> %0\;cvt.d.w\\t%0,%0")


(define_insn "floatsisf2"
  [(set (match_operand:SF 0 "register_operand" "=f")
	(float:SF (match_operand:SI 1 "register_operand" "r")))]
  ""
  "mtc1\\t%1,%0\\t\\t#floatsisf2\\t%1 -> %0\;cvt.s.w\\t%0,%0")

(define_expand "fixuns_truncdfsi2"
  [(set (match_operand:SI 0 "register_operand" "")
	(unsigned_fix:SI (match_operand:DF 1 "register_operand" "")))]
  ""
  "
{
  rtx reg1 = gen_reg_rtx (DFmode);
  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);

  if (reg1)			/* turn off complaints about unreached code */
    {
      emit_move_insn (reg1, immed_real_const_1 (offset, DFmode));
      do_pending_stack_adjust ();
      emit_insn (gen_rtx (SET, VOIDmode, cc0_rtx,
			  gen_rtx (COMPARE, DFmode, operands[1], 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], reg1));
      emit_move_insn (reg3, gen_rtx (CONST_INT, VOIDmode, 0x80000000));

      emit_insn (gen_fix_truncdfsi2 (operands[0], reg2));
      emit_insn (gen_iorsi3 (operands[0], 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" "")
	(unsigned_fix:SI (match_operand:SF 1 "register_operand" "")))]
  ""
  "
{
  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);

  if (reg1)			/* turn off complaints about unreached code */
    {
      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_move_insn (reg3, gen_rtx (CONST_INT, VOIDmode, 0x80000000));

      emit_insn (gen_fix_truncsfsi2 (operands[0], reg2));
      emit_insn (gen_iorsi3 (operands[0], 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;
    }
}")

					;;- Wild things used when
					;;- unions make double and int
					;;- overlap.
					;;-
					;;- This must be supported
					;;- since corresponding code
					;;- gets generated

(define_insn ""
  [(set (subreg:DF (match_operand:DI 0 "register_operand" "=ry") 0)
	(match_operand:DF 1  "register_operand" "rf"))
   (clobber (match_operand  2  "register_operand" "rf"))]
  ""
  "mfc1\\t%0,%L1\;mfc1\\t%D0,%M1")

(define_insn ""
  [(set (subreg:DF (match_operand:DI 0 "register_operand" "=ry") 0)
	(match_operand:DF 1  "register_operand" "rf"))]
  ""
  "mfc1\\t%0,%L1\;mfc1\\t%D0,%M1")

(define_insn ""
  [(set (match_operand:DF 0 "register_operand" "=rf")
        (subreg:DF (match_operand:DI 1 "register_operand" "ry") 0))
   (clobber (match_operand  2  "register_operand" "rf"))]
  ""
  "mfc1\\t%0,%L1\;mfc1\\t%D0,%M1")

(define_insn ""
  [(set (match_operand:DF 0 "register_operand" "=rf")
        (subreg:DF (match_operand:DI 1 "register_operand" "ry") 0))]
  ""
  "mfc1\\t%0,%L1\;mfc1\\t%D0,%M1")

;;
;;  ....................
;;
;;          MOVES
;;
;;          and
;;
;;          LOADS AND STORES
;;
;;  ....................

;; unaligned word moves generated to move unaligned structs
;; to/from registers.
;; We use (use (reg:SI 0)) to select this pattern rather than the
;; normal movsi.  Make these before the normal move patterns so they
;; match first.

(define_expand "movsi_unaligned"
  [(parallel [(set (match_operand:SI 0 "general_operand" "")
		   (match_operand:SI 1 "general_operand" ""))
	      (use (reg:SI 0))])]
  ""
  "
{
  extern rtx force_reg ();
  extern rtx gen_movsi_ulw ();
  extern rtx gen_movsi ();

  if (GET_CODE (operands[0]) == MEM && !register_operand (operands[1], SImode))
    {
      rtx reg = gen_reg_rtx (SImode);

      emit_insn (gen_movsi_ulw (reg, operands[1]));
      operands[1] = reg;
    }

  /* Generate appropriate load, store.  If not a load or store,
     do a normal movsi.  */
  if (GET_CODE (operands[0]) != MEM && GET_CODE (operands[1]) != MEM)
    {
      emit_insn (gen_movsi (operands[0], operands[1]));
      DONE;
    }

  /* Fall through and generate normal code.  */
}")

(define_insn "movsi_ulw"
  [(set (match_operand:SI 0 "register_operand" "=&r,r,r")
	(match_operand:SI 1 "general_operand" "o,r,n"))
   (use (reg:SI 0))]
  ""
  "*
{
  extern rtx eliminate_constant_term ();

  switch (which_alternative)
    {
    case 0:			/* reg <- memory */
      {
	int offset = 0;
	rtx addr = XEXP (operands[1], 0);
	rtx mem_addr = eliminate_constant_term (addr, &offset);

	/* The stack/frame pointers are always aligned, so we can convert
	   to the faster lw if we are referencing an aligned stack location.  */

	if ((offset & ~3) == 0
	    && (mem_addr == stack_pointer_rtx || mem_addr == frame_pointer_rtx))
	  return \"lw\\t%0,%1\";

	if (TARGET_GAS)
	  {
	    enum rtx_code code = GET_CODE (addr);
	    if (code == CONST || code == SYMBOL_REF || code == LABEL_REF)
	      {
		operands[2] = gen_rtx (REG, SImode, GP_REG_FIRST + 1);
		return \".set\\tnoat\\n\\tla\\t%2,%1\;ulw\\t%0,0(%2)\\n\\t.set\\tat\";
	      }
	  }
      }

      return \"ulw\\t%0,%1\";

    case 1:			/* reg <- reg */
      return \"move\\t%0,%1\";

    case 2:			/* reg <- integer constant */
      return \"li\\t%0,%1\";
    }
}")

(define_insn "movsi_usw"
  [(set (match_operand:SI 0 "memory_operand" "=&o")
	(match_operand:SI 1 "register_operand" "r"))
   (use (reg:SI 0))]
  ""
  "*
{
  extern rtx eliminate_constant_term ();
  int offset = 0;
  rtx addr = XEXP (operands[0], 0);
  rtx mem_addr = eliminate_constant_term (addr, &offset);

  /* The stack/frame pointers are always aligned, so we can convert
     to the faster sw if we are referencing an aligned stack location.
     At present, it is not recognized if the frame pointer is omitted.  */

  if ((offset & ~3) == 0
      && (mem_addr == stack_pointer_rtx || mem_addr == frame_pointer_rtx))
    return \"sw\\t%1,%0\";

  if (TARGET_GAS)
    {
      enum rtx_code code = GET_CODE (XEXP (operands[0], 0));
      if (code == CONST || code == SYMBOL_REF || code == LABEL_REF)
	{
	  operands[2] = gen_rtx (REG, SImode, GP_REG_FIRST + 1);
	  return \".set\\tnoat\\n\\tla\\t%2,%0\;usw\\t%1,0(%2)\\n\\t.set\\tat\";
	}
    }

  return \"usw\\t%1,%0\";
}")

(define_insn "movdi"
  [(set (match_operand:DI 0 "general_operand" "=r,*r,*m")
	(match_operand:DI 1 "general_operand" "r,*miF,*r"))]
  ""
  "*
{
  extern rtx adj_offsettable_operand ();
  extern int offsettable_address_p ();

  if (which_alternative == 0)
    {
      /* Move REGISTER <- REGISTER */
      if (REGNO (operands[0]) != (REGNO (operands[1])+1))
	return \"move\\t%0,%1\\n\\tmove\\t%D0,%D1\";
      else
	return \"move\\t%D0,%D1\\n\\tmove\\t%0,%1\";
    }

  else if (which_alternative == 1)
    {
      if (GET_CODE (operands[1]) == MEM)
	{
	  /* REGISTER <- MEMORY */
	  if (offsettable_address_p (1, DImode, XEXP (operands[1], 0)))
	    {
	      operands[2] = adj_offsettable_operand (operands[1], 4);
	      return \"lw\\t%0,%1\;lw\\t%D0,%2\";
	    }

	  else
	    {
	      operands[2] = gen_rtx (REG, Pmode, 1);
	      return \".set\\tnoat\;la\\t%2,%1\;lw\\t%0,0(%2)\;lw\\t%D0,4(%2)\;set\\tat\";
	    }
	}

      /* REGISTER <- small integer constant */
      else if (CONSTANT_P (operands[1]))
	{
	  operands[2] = gen_rtx (CONST_INT, VOIDmode, INTVAL (operands[1]) >= 0 ? 0 : -1);
	  return \"li\\t%M0,%2\;li\\t%L0,%1\";
	}

      /* Register <- large integer constant */
      else if (GET_CODE (operands[1]) == CONST_DOUBLE)
	{
	  operands[2] = gen_rtx (CONST_INT, VOIDmode, CONST_DOUBLE_LOW (operands[1]));
	  operands[3] = gen_rtx (CONST_INT, VOIDmode, CONST_DOUBLE_HIGH (operands[1]));
	  return \"li\\t%M0,%3\;li\\t%L0,%2\";
	}
    }

  else if (which_alternative == 2 && GET_CODE (operands[0]) == MEM)
    {
      /* Memory <- Register */
      if (offsettable_address_p (1, DImode, XEXP (operands[0], 0)))
	{
	  operands[2] = adj_offsettable_operand (operands[0], 4);
	  return \"sw\\t%1,%0\;sw\\t%D1,%2\";
	}

      else
	{
	  operands[2] = gen_rtx (REG, Pmode, 1);
	  return \".set\\tnoat\;la\\t%2,%0\;sw\\t%1,0(%2)\;sw\\t%D1,4(%2)\;set\\tat\";
	}
    }

  abort_with_insn (insn, \"impossible case in movdi\");
  return \"\";
}")

(define_insn "movsi"
  [(set (match_operand:SI 0 "general_operand" "=r,r,m,r,r,m,*r")
	(match_operand:SI 1 "general_operand" "r,m,r,i,J,J,*p"))]
  ""
  "*
{
  enum rtx_code code0 = GET_CODE (operands[0]);
  enum rtx_code code1 = GET_CODE (operands[1]);

  if (code0 == REG && code1 == REG)
    return \"move\\t%0,%1\";

  else if (code0 == REG && code1 == MEM)
    return \"lw\\t%0,%1\";

  else if (code0 == MEM && code1 == REG)
    return \"sw\\t%1,%0\";

  else if (code0 == REG && code1 == CONST_INT)
    return \"li\\t%0,%1\";

  else if (code0 == MEM && code1 == CONST_INT && INTVAL (operands[1]) == 0)
    return \"sw\\t$0,%0\";

  else if (code0 == REG && CONSTANT_P (operands[1]))
    return \"la\\t%0,%a1\";

  else if (code0 == REG && code1 == PLUS
	   && GET_CODE (XEXP (operands[1], 0)) == REG
	   && GET_CODE (XEXP (operands[1], 1)) == CONST_INT)
    {
      operands[2] = XEXP (operands[1], 0);
      operands[3] = XEXP (operands[1], 1);
      return \"add%:\\t%0,%2,%3\";
    }

  abort_with_insn (insn, \"Bad movsi\");
  return 0;
}")

(define_insn "movhi"
  [(set (match_operand:HI 0 "general_operand" "=r,r,m,r,r,m")
	(match_operand:HI 1 "general_operand" "r,m,r,i,J,J"))]
  ""
  "*
{
  enum rtx_code code0 = GET_CODE (operands[0]);
  enum rtx_code code1 = GET_CODE (operands[1]);

  if (code0 == REG && code1 == REG)
    return \"move\\t%0,%1\";

  else if (code0 == REG && code1 == MEM)
    return \"lh\\t%0,%1\";

  else if (code0 == MEM && code1 == REG)
    return \"sh\\t%1,%0\";

  else if (code0 == REG && code1 == CONST_INT)
    return \"li\\t%0,%1\";

  else if (code0 == MEM && code1 == CONST_INT && INTVAL (operands[1]) == 0)
    return \"sh\\t$0,%0\";

  else if (code0 == REG && CONSTANT_P (operands[1]))
    return \"la\\t%0,%a1\";

  else if (code0 == REG && code1 == PLUS
	   && GET_CODE (XEXP (operands[1], 0)) == REG
	   && GET_CODE (XEXP (operands[1], 1)) == CONST_INT)
    {
      operands[2] = XEXP (operands[1], 0);
      operands[3] = XEXP (operands[1], 1);
      return \"add%:\\t%0,%2,%3\";
    }

  abort_with_insn (insn, \"Bad movhi\");
  return 0;
}")

(define_insn "movqi"
  [(set (match_operand:QI 0 "general_operand" "=r,r,m,r,r,m")
	(match_operand:QI 1 "general_operand" "r,m,r,i,J,J"))]
  ""
  "*
{
  enum rtx_code code0 = GET_CODE (operands[0]);
  enum rtx_code code1 = GET_CODE (operands[1]);

  if (code0 == REG && code1 == REG)
    return \"move\\t%0,%1\";

  else if (code0 == REG && code1 == MEM)
    return \"lb\\t%0,%1\";

  else if (code0 == MEM && code1 == REG)
    return \"sb\\t%1,%0\";

  else if (code0 == REG && code1 == CONST_INT)
    return \"li\\t%0,%1\";

  else if (code0 == MEM && code1 == CONST_INT && INTVAL (operands[1]) == 0)
    return \"sb\\t$0,%0\";

  else if (code0 == REG && CONSTANT_P (operands[1]))
    return \"la\\t%0,%a1\";

  else if (code0 == REG && code1 == PLUS
	   && GET_CODE (XEXP (operands[1], 0)) == REG
	   && GET_CODE (XEXP (operands[1], 1)) == CONST_INT)
    {
      operands[2] = XEXP (operands[1], 0);
      operands[3] = XEXP (operands[1], 1);
      return \"add%:\\t%0,%2,%3\";
    }

  abort_with_insn (insn, \"Bad movqi\");
  return 0;
}")

(define_insn "movsf"
  [(set (match_operand:SF 0 "general_operand" "=f,f,m,fy,*f,*y,*y,*m")
	(match_operand:SF 1 "general_operand" "f,m,f,F,*y,*f,*m,*y"))]
  ""
  "*
{
  enum rtx_code code0 = GET_CODE (operands[0]);
  enum rtx_code code1 = GET_CODE (operands[1]);

  if (code0 == REG)
    {
      if (code1 == REG)
	{
	  if (FP_REG_P (REGNO (operands[0])))
	    {
	      if (FP_REG_P (REGNO (operands[1])))
		return \"mov.s\\t%0,%1\";
	      else
		return \"mtc1\\t%1,%0\\t\\t# Calling sequence trick\";
	    }

	  else if (FP_REG_P (REGNO (operands[1])))
	    return \"mfc1\\t%0,%1\\t\\t# Calling sequence trick\";

	  else
	    return \"move\\t%0,%1\";
	}

      else if (code1 == CONST_DOUBLE)
	return \"li.s\\t%0,%1\";

      else if (code1 == MEM)
	return (GP_REG_P (REGNO (operands[0]))) ? \"lw\\t%0,%1\" : \"l.s\\t%0,%1\";
    }

  else if (code0 == MEM && code1 == REG)
    return (GP_REG_P (REGNO (operands[1]))) ? \"sw\\t%1,%0\" : \"s.s\\t%1,%0\";

  abort_with_insn (insn, \"Bad movsf\");
  return \"\";
}")


(define_insn "movdf"
  [(set (match_operand:DF 0 "general_operand" "=f,f,m,fy,*f,*y,&*y,*m")
	(match_operand:DF 1 "general_operand" "f,m,f,F,*y,*f,*m,*y"))]
  ""
  "*
{
  extern rtx adj_offsettable_operand ();
  extern int offsettable_address_p ();

  enum rtx_code code0 = GET_CODE (operands[0]);
  enum rtx_code code1 = GET_CODE (operands[1]);

  if (code0 == REG)
    {
      if (code1 == REG)
	{
	  if (FP_REG_P (REGNO (operands[0])))
	    {
	      if (FP_REG_P (REGNO (operands[1])))
		return \"mov.d\\t%0,%1\";
	      else
		return \"mtc1\\t%L1,%0\\t\\t# Calling sequence trick\;mtc1\\t%M1,%D0\";
	    }

	  else if (FP_REG_P (REGNO (operands[1])))
	    return \"mfc1\\t%L0,%1\\t\\t# Calling sequence trick\;mfc1\\t%M0,%D1\";

	  else if (REGNO (operands[0]) != (REGNO (operands[1])+1))
	    return \"move\\t%0,%1\\n\\tmove\\t%D0,%D1\";

	  else
	    return \"move\\t%D0,%D1\\n\\tmove\\t%0,%1\";
	}

      else if (code1 == CONST_DOUBLE)
	return \"li.d\\t%0,%1\";

      else if (code1 == MEM)
	{
	  if (FP_REG_P (REGNO (operands[0])))
	    return \"l.d\\t%0,%1\";

	  else if (offsettable_address_p (1, DFmode, XEXP (operands[1], 0)))
	    {
	      operands[2] = adj_offsettable_operand (operands[1], 4);
	      if (reg_mentioned_p (operands[0], operands[1]))
		return \"lw\\t%D0,%2\;lw\\t%0,%1\";
	      else
		return \"lw\\t%0,%1\;lw\\t%D0,%2\";
	    }

	  else
	    {
	      operands[2] = gen_rtx (REG, Pmode, 1);
	      return \".set\\tnoat\;la\\t%2,%1\;lw\\t%0,0(%2)\;lw\\t%D0,4(%2)\;set\\tat\";
	    }
	}
    }

  else if (code0 == MEM && code1 == REG)
    {
      if (FP_REG_P (REGNO (operands[1])))
	return \"s.d\\t%1,%0\";

      else if (offsettable_address_p (1, DFmode, XEXP (operands[0], 0)))
	{
	  operands[2] = adj_offsettable_operand (operands[0], 4);
	  return \"sw\\t%1,%0\;sw\\t%D1,%2\";
	}

      else
	{
	  operands[2] = gen_rtx (REG, Pmode, 1);
	  return \".set\\tnoat\;la\\t%2,%0\;sw\\t%1,0(%2)\;sw\\t%D1,4(%2)\;set\\tat\";
	}
    }

  abort_with_insn (insn, \"Bad movdf\");
  return \"\";
}")


;;
;;  ....................
;;
;;          OTHER ARITHMETIC AND SHIFT
;;
;;  ....................

(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)
    operands[2] = gen_rtx (CONST_INT, VOIDmode, (XINT (operands[2], 0))& 0x1f);

  return \"sll\\t%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)
    operands[2] = gen_rtx (CONST_INT, VOIDmode, (XINT (operands[2], 0))& 0x1f);

  return \"sra\\t%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)
    operands[2] = gen_rtx (CONST_INT, VOIDmode, (XINT (operands[2], 0))& 0x1f);

  return \"srl\\t%0,%1,%2\";
}")

(define_insn "negsi2"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(neg:SI (match_operand:SI 1 "register_operand" "r")))]
  ""
  "sub%:\\t%0,$0,%1")

(define_insn "negdf2"
  [(set (match_operand:DF 0 "register_operand" "=f")
	(neg:DF (match_operand:DF 1 "register_operand" "f")))]
  ""
  "neg.d\\t%0,%1")

(define_insn "negsf2"

  [(set (match_operand:SF 0 "register_operand" "=f")
	(neg:SF (match_operand:SF 1 "register_operand" "f")))]
  ""
  "neg.s\\t%0,%1")


(define_insn "one_cmplsi2"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(not:SI (match_operand:SI 1 "register_operand" "r")))]
  ""
  "nor\\t%0,$0,%1")

;;
;;  ....................
;;
;;          COMPARISONS
;;
;;  ....................

					;;- Order is significant here
					;;- because there are untyped
					;;- comparisons generated by
					;;- the optimizer
                                        ;;- (set (cc0)
                                        ;;-      (compare (const_int 2)
                                        ;;-           (const_int 1)))

(define_insn "cmpsi"
  [(set (cc0)
	(compare (match_operand:SI 0 "register_operand" "r")
		 (match_operand:SI 1 "arith_operand" "rI")))]
  ""
  "*
    compare_collect (SImode, operands[0], operands[1]);
    return \"\\t\\t\\t\\t# cmpsi\\t%0,%1\";
")


(define_insn ""
  [(set (cc0)
	(match_operand:SI 0 "register_operand" "r"))]
  ""
  "*
    compare_collect (SImode, operands[0], gen_rtx (REG, SImode, 0));
    return \"\\t\\t\\t\\t# (set (cc0)\\t%0)\";
")

;; These patterns are hopelessly invalid, because
;; comparing subword values properly requires extending them.

;; (define_insn "cmphi"
;;   [(set (cc0)
;; 	(compare (match_operand:HI 0 "register_operand" "r")
;; 		 (match_operand:HI 1 "register_operand" "r")))]
;;   ""
;;   "*
;;     compare_collect (HImode, operands[0], operands[1]);
;;     return      \" #\\tcmphi\\t%0,%1\";
;;   ")
;; 
;; (define_insn "cmpqi"
;;   [(set (cc0)
;; 	(compare (match_operand:QI 0 "register_operand" "r")
;; 		 (match_operand:QI 1 "register_operand" "r")))]
;;   ""
;;   "*
;;     compare_collect (QImode, operands[0], operands[1]);
;;     return      \" #\\tcmpqi\\t%0,%1\";
;;   ")
;; 
;; (define_insn ""
;;   [(set (cc0)
;; 	(match_operand:QI 0 "register_operand" "r"))]
;;   ""
;;   "*
;;     compare_collect (QImode, operands[0], gen_rtx (REG, QImode, 0));
;;     return \" #\\t (set (cc0)\\t%0)\";
;; ")
;; 
;; (define_insn ""
;;   [(set (cc0)
;; 	(match_operand:HI 0 "register_operand" "r"))]
;;   ""
;;   "*
;;     compare_collect (HImode, operands[0], gen_rtx (REG, HImode, 0));
;;     return \" #\\t (set (cc0)\\t%0)\";
;; ")

(define_insn "cmpdf"
  [(set (cc0)
	(compare (match_operand:DF 0 "register_operand" "f")
		 (match_operand:DF 1 "register_operand" "f")))]
  ""
  "*
    compare_collect (DFmode, operands[0], operands[1]);
    return \" #\\t\\t\\t\\tcmpdf\\t%0,%1\" ;
")

(define_insn "cmpsf"
  [(set (cc0)
	(compare (match_operand:SF 0 "register_operand" "f")
		 (match_operand:SF 1 "register_operand" "f")))]
  ""
  "*
    compare_collect (SFmode, operands[0], operands[1]);
    return \"\\t\\t\\t\\t# cmpsf\\t%0,%1\" ;
")

;;
;;  ....................
;;
;;          BRANCHES
;;
;;  ....................

(define_insn "jump"
  [(set (pc)
	(label_ref (match_operand 0 "" "")))]
  ""
  "*
{
  if (GET_CODE (operands[0]) == REG)
    return \"j\\t%0\";
  else
    return \"j\\t%l0\";
}")


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


(define_insn "beq"
  [(set (pc)
	(if_then_else (eq (cc0)
			  (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "*
{
  rtx br_ops[3];
  enum machine_mode mode;
  compare_restore (br_ops,  &mode, insn);
  br_ops[2] = operands[0];
  if (mode == DFmode)
    {
      output_asm_insn (\"c.eq.d\\t%0,%1\\t\\t# beq\", br_ops);
      output_asm_insn (\"bc1t\\t%2\\t\\t# beq\", br_ops);
    }
  else if  (mode == SFmode)
    {
      output_asm_insn (\"c.eq.s\\t%0,%1\\t\\t# beq\", br_ops);
      output_asm_insn (\"bc1t\\t%2\\t\\t# beq\", br_ops);
    }
  else
    {
      output_asm_insn (\"beq\\t%0,%1,%2\\t\\t# beq\", br_ops);
    }
  return \"\";
}
   ")

(define_insn "bne"
  [(set (pc)
	(if_then_else (ne (cc0)
			  (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "*
{
  rtx br_ops[3];
  enum machine_mode mode;
  compare_restore (br_ops,  &mode, insn);
  br_ops[2] = operands[0];
  if (mode == DFmode)
    {
      output_asm_insn (\"c.eq.d\\t%0,%1\\t\\t# bne\", br_ops);
      output_asm_insn (\"bc1f\\t%2\\t\\t# bne\", br_ops);
    }
  else if  (mode == SFmode)
    {
      output_asm_insn (\"c.eq.s\\t%0,%1\\t\\t# bne\", br_ops);
      output_asm_insn (\"bc1f\\t%2\\t\\t# bne\", br_ops);
    }
  else
    {
      output_asm_insn (\"bne\\t%0,%1,%2\\t\\t# bne\", br_ops);
    }
  return \"\";
}

")

(define_insn "bgt"
  [(set (pc)
	(if_then_else (gt (cc0)
			  (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "*
{
  rtx br_ops[3];
  enum machine_mode mode;
  compare_restore (br_ops,  &mode, insn);
  br_ops[2] = operands[0];
  if (mode == DFmode)
    {
      output_asm_insn (\"c.le.d\\t%0,%1\\t\\t# bgt branch %0 > %1\", br_ops);
      output_asm_insn (\"bc1f\\t%2\\t\\t# bgt\", br_ops);
    }
  else if  (mode == SFmode)
    {
      output_asm_insn (\"c.le.s\\t%0,%1\\t\\t# bgt branch %0 > %1\", br_ops);
      output_asm_insn (\"bc1f\\t%2\\t\\t# bgt\", br_ops);
    }
  else
    {
      output_asm_insn (\"bgt\\t%0,%1,%2\\t\\t# bgt\", br_ops);
    }
  return \"\";
}

")

(define_insn "blt"
  [(set (pc)
	(if_then_else (lt (cc0)
			  (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "*
{
  rtx br_ops[3];
  enum machine_mode mode;
  compare_restore (br_ops,  &mode, insn);
  br_ops[2] = operands[0];
  if (mode == DFmode)
    {
      output_asm_insn (\"c.lt.d\\t%0,%1\\t\\t# blt\", br_ops);
      output_asm_insn (\"bc1t\\t%2\\t\\t# blt\", br_ops);
    }
  else if  (mode == SFmode)
    {
      output_asm_insn (\"c.lt.s\\t%0,%1\\t\\t# blt\", br_ops);
      output_asm_insn (\"bc1t\\t%2\\t\\t# blt\", br_ops);
    }
  else
    {
      output_asm_insn (\"blt\\t%0,%1,%2\\t\\t# blt\", br_ops);
    }
  return \" #\\tblt\\t%l0\\t\\t# blt\";
}
")

(define_insn "bgtu"
  [(set (pc)
	(if_then_else (gtu (cc0)
			   (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "*
{
  rtx br_ops[3];
  enum machine_mode mode;
  compare_restore (br_ops,  &mode, insn);
  br_ops[2] = operands[0];
  if (mode == DFmode)
    {
      output_asm_insn (\"c.le.d\\t%0,%1\\t\\t# bgtu\", br_ops);
      output_asm_insn (\"bc1f\\t%2\\t\\t# bgtu\", br_ops);
    }
  else if  (mode == SFmode)
    {
      output_asm_insn (\"c.le.s\\t%0,%1\\t\\t# bgtu\", br_ops);
      output_asm_insn (\"bc1f\\t%2\\t\\t# bgtu\", br_ops);
    }
  else
    {
      output_asm_insn (\"bgtu\\t%0,%1,%2\\t\\t# bgtu\", br_ops);
    }
  return \" #\\tbgtu\\t%l0\\t\\t# bgtu\";
}
")

(define_insn "bltu"
  [(set (pc)
	(if_then_else (ltu (cc0)
			   (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "*
{
  rtx br_ops[3];
  enum machine_mode mode;
  compare_restore (br_ops,  &mode, insn);
  br_ops[2] = operands[0];
  if (mode == DFmode)
    {
      output_asm_insn (\"c.lt.d\\t%0,%1\\t\\t# bltu\", br_ops);
      output_asm_insn (\"bc1t\\t%2\\t\\t# bltu\", br_ops);
    }
  else if  (mode == SFmode)
    {
      output_asm_insn (\"c.lt.s\\t%0,%1\\t\\t# bltu\", br_ops);
      output_asm_insn (\"bc1t\\t%2\\t\\t# bltu\", br_ops);
    }
  else
    {
      output_asm_insn (\"bltu\\t%0,%1,%2\\t\\t# bltu\", br_ops);
    }
  return \"\";
}
")

(define_insn "bge"
  [(set (pc)
	(if_then_else (ge (cc0)
			  (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "*
{
  rtx br_ops[3];
  enum machine_mode mode;
  compare_restore (br_ops,  &mode, insn);
  br_ops[2] = operands[0];
  if (mode == DFmode)
    {
      output_asm_insn (\"c.lt.d\\t%0,%1\\t\\t# bge\", br_ops);
      output_asm_insn (\"bc1f\\t%2\\t\\t# bge\", br_ops);
    }
  else if  (mode == SFmode)
    {
      output_asm_insn (\"c.lt.s\\t%0,%1\\t\\t# bge\", br_ops);
      output_asm_insn (\"bc1f\\t%2\\t\\t# bge\", br_ops);
    }
  else
    {
      output_asm_insn (\"bge\\t%0,%1,%2\\t\\t# bge\", br_ops);
    }
  return \"\";
}
")

(define_insn "bgeu"
  [(set (pc)
	(if_then_else (geu (cc0)
			   (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "*
{
  rtx br_ops[3];
  enum machine_mode mode;
  compare_restore (br_ops,  &mode, insn);
  br_ops[2] = operands[0];
  if (mode == DFmode)
    {
      output_asm_insn (\"c.lt.d\\t%0,%1\\t\\t# bgeu\", br_ops);
      output_asm_insn (\"bc1f\\t%2\\t\\t# bgeu\", br_ops);
    }
  else if  (mode == SFmode)
    {
      output_asm_insn (\"c.lt.s\\t%0,%1\\t\\t# bgeu\", br_ops);
      output_asm_insn (\"bc1f\\t%2\\t\\t# bgeu\", br_ops);
    }
  else
    {
      output_asm_insn (\"bgeu\\t%0,%1,%2\\t\\t# bgeu\", br_ops);
    }
  return \"\";
}
")

(define_insn "ble"
  [(set (pc)
	(if_then_else (le (cc0)
			  (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "*
{
  rtx br_ops[3];
  enum machine_mode mode;
  compare_restore (br_ops,  &mode, insn);
  br_ops[2] = operands[0];
  if (mode == DFmode)
    {
      output_asm_insn (\"c.le.d\\t%0,%1\\t\\t# ble\", br_ops);
      output_asm_insn (\"bc1t\\t%2\\t\\t# ble\", br_ops);
    }
  else if  (mode == SFmode)
    {
      output_asm_insn (\"c.le.s\\t%0,%1\\t\\t# ble\", br_ops);
      output_asm_insn (\"bc1t\\t%2\\t\\t# ble\", br_ops);
    }
  else
    {
      output_asm_insn (\"ble\\t%0,%1,%2\\t\\t# ble\", br_ops);
    }
  return \"\";
}
")

(define_insn "bleu"
  [(set (pc)
	(if_then_else (leu (cc0)
			  (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "*
{
  rtx br_ops[3];
  enum machine_mode mode;
  compare_restore (br_ops,  &mode, insn);
  br_ops[2] = operands[0];
  if (mode == DFmode)
    {
      output_asm_insn (\"c.le.d\\t%0,%1\\t\\t# ble\", br_ops);
      output_asm_insn (\"bc1t\\t%2\\t\\t# ble\", br_ops);
    }
  else if  (mode == SFmode)
    {
      output_asm_insn (\"c.le.s\\t%0,%1\\t\\t# ble\", br_ops);
      output_asm_insn (\"bc1t\\t%2\\t\\t# ble\", br_ops);
    }
  else
    {
      output_asm_insn (\"bleu\\t%0,%1,%2\\t\\t# bleu\", br_ops);
    }
  return \" #\\tbleu\\t%l0\\t\\t# bleu\";
}
")

(define_insn ""
  [(set (pc)
	(if_then_else (ne (cc0)
			  (const_int 0))
		      (pc)
		      (label_ref (match_operand 0 "" ""))))]
  ""
  "*
{
  rtx br_ops[3];
  enum machine_mode mode;
  compare_restore (br_ops,  &mode, insn);
  br_ops[2] = operands[0];
  if (mode == DFmode)
    {
      output_asm_insn (\"c.eq.d\\t%0,%1\\t\\t# beq\", br_ops);
      output_asm_insn (\"bc1t\\t%2\\t\\t# beq\", br_ops);
    }
  else if  (mode == SFmode)
    {
      output_asm_insn (\"c.eq.s\\t%0,%1\\t\\t# beq\", br_ops);
      output_asm_insn (\"bc1t\\t%2\\t\\t# beq\", br_ops);
    }
  else
    {
      output_asm_insn (\"beq\\t%0,%1,%2\\t\\t# beq Inv.\", br_ops);
    }
  return \"\";
}
")

(define_insn ""
  [(set (pc)
	(if_then_else (eq (cc0)
			  (const_int 0))
		      (pc)
		      (label_ref (match_operand 0 "" ""))))]
  ""
  "*
{
  rtx br_ops[3];
  enum machine_mode mode;
  compare_restore (br_ops,  &mode, insn);
  br_ops[2] = operands[0];
  if (mode == DFmode)
    {
      output_asm_insn (\"c.eq.d\\t%0,%1\\t\\t# bne\", br_ops);
      output_asm_insn (\"bc1f\\t%2\\t\\t# bne\", br_ops);
    }
  else if  (mode == SFmode)
    {
      output_asm_insn (\"c.eq.s\\t%0,%1\\t\\t# bne\", br_ops);
      output_asm_insn (\"bc1f\\t%2\\t\\t# beq\", br_ops);
    }
  else
    {
      output_asm_insn (\"bne\\t%0,%1,%2\\t\\t# bne Inv.\", br_ops);
    }
  return \"\";
}

")

(define_insn ""
  [(set (pc)
	(if_then_else (le (cc0)
			  (const_int 0))
		      (pc)
		      (label_ref (match_operand 0 "" ""))))]
  ""
  "*
{
  rtx br_ops[3];
  enum machine_mode mode;
  compare_restore (br_ops,  &mode, insn);
  br_ops[2] = operands[0];
  if (mode == DFmode)
    {
      output_asm_insn (\"c.le.d\\t%0,%1\\t\\t# bgt\", br_ops);
      output_asm_insn (\"bc1f\\t%2\\t\\t# beq\", br_ops);
    }
  else if  (mode == SFmode)
    {
      output_asm_insn (\"c.le.s\\t%0,%1\\t\\t# bgt\", br_ops);
      output_asm_insn (\"bc1f\\t%2\\t\\t# beq\", br_ops);
    }
  else
    {
      output_asm_insn (\"bgt\\t%0,%1,%2\\t\\t# bgt Inv.\", br_ops);
    }
  return \"\";
}
")

(define_insn ""
  [(set (pc)
	(if_then_else (leu (cc0)
			   (const_int 0))
		      (pc)
		      (label_ref (match_operand 0 "" ""))))]
  ""
  "*
{
  rtx br_ops[3];
  enum machine_mode mode;
  compare_restore (br_ops,  &mode, insn);
  br_ops[2] = operands[0];
  if (mode == DFmode)
    {
      output_asm_insn (\"c.le.d\\t%0,%1\\t\\t# bgt\", br_ops);
      output_asm_insn (\"bc1f\\t%2\\t\\t# beq\", br_ops);
    }
  else if  (mode == SFmode)
    {
      output_asm_insn (\"c.le.s\\t%0,%1\\t\\t# bgt\", br_ops);
      output_asm_insn (\"bc1f\\t%2\\t\\t# beq\", br_ops);
    }
  else
    {
      output_asm_insn (\"bgtu\\t%0,%1,%2\\t\\t# bgtu Inv.\", br_ops);
    }
  return \" #\\tbgtu\\t%l0\\t\\t# bgtu\";
}
")

(define_insn ""
  [(set (pc)
	(if_then_else (ge (cc0)
			  (const_int 0))
		      (pc)
		      (label_ref (match_operand 0 "" ""))))]
  ""
  "*
{
  rtx br_ops[3];
  enum machine_mode mode;
  compare_restore (br_ops,  &mode, insn);
  br_ops[2] = operands[0];
  if (mode == DFmode)
    {
      output_asm_insn (\"c.lt.d\\t%0,%1\\t\\t# blt\", br_ops);
      output_asm_insn (\"bc1t\\t%2\\t\\t# beq\", br_ops);
    }
  else if  (mode == SFmode)
    {
      output_asm_insn (\"c.lt.s\\t%0,%1\\t\\t# blt\", br_ops);
      output_asm_insn (\"bc1t\\t%2\\t\\t# beq\", br_ops);
    }
  else
    {
      output_asm_insn (\"blt\\t%0,%1,%2\\t\\t# blt Inv.\", br_ops);
    }
  return \"\";
}
")

(define_insn ""
  [(set (pc)
	(if_then_else (geu (cc0)
			   (const_int 0))
		      (pc)
		      (label_ref (match_operand 0 "" ""))))]
  ""
  "*
{
  rtx br_ops[3];
  enum machine_mode mode;
  compare_restore (br_ops,  &mode, insn);
  br_ops[2] = operands[0];
  if (mode == DFmode)
    {
      output_asm_insn (\"c.lt.d\\t%0,%1\\t\\t# bltu\", br_ops);
      output_asm_insn (\"bc1t\\t%2\\t\\t# bltu\", br_ops);
    }
  else if  (mode == SFmode)
    {
      output_asm_insn (\"c.lt.s\\t%0,%1\\t\\t# bltu\", br_ops);
      output_asm_insn (\"bc1t\\t%2\\t\\t# bltu\", br_ops);
    }
  else
    {
      output_asm_insn (\"bltu\\t%0,%1,%2\\t\\t# bltu Inv.\", br_ops);
    }
  return \" #\\tbltu\\t%l0\\t\\t# bltu\";
}
")

(define_insn ""
  [(set (pc)
	(if_then_else (lt (cc0)
			  (const_int 0))
		      (pc)
		      (label_ref (match_operand 0 "" ""))))]
  ""
  "*
{
  rtx br_ops[3];
  enum machine_mode mode;
  compare_restore (br_ops,  &mode, insn);
  br_ops[2] = operands[0];
  if (mode == DFmode)
    {
      output_asm_insn (\"c.lt.d\\t%0,%1\\t\\t# bge\", br_ops);
      output_asm_insn (\"bc1f\\t%2\\t\\t# bge (DF) Inv.\", br_ops);
    }
  else if  (mode == SFmode)
    {
      output_asm_insn (\"c.lt.s\\t%0,%1\\t\\t# bge\", br_ops);
      output_asm_insn (\"bc1f\\t%2\\t\\t# bge (SF) Inv.\", br_ops);
    }
  else
    {
      output_asm_insn (\"bge\\t%0,%1,%2\\t\\t# bge Inv.\", br_ops);
    }
  return \"\";
}
")

(define_insn ""
  [(set (pc)
	(if_then_else (ltu (cc0)
			   (const_int 0))
		      (pc)
		      (label_ref (match_operand 0 "" ""))))]
  ""
  "*
{
  rtx br_ops[3];
  enum machine_mode mode;
  compare_restore (br_ops,  &mode, insn);
  br_ops[2] = operands[0];
  if (mode == DFmode)
    {
      output_asm_insn (\"c.lt.d\\t%0,%1\\t\\t# bge\", br_ops);
      output_asm_insn (\"bc1f\\t%2\\t\\t# bgeu (DF)  Inv.\", br_ops);
    }
  else if  (mode == SFmode)
    {
      output_asm_insn (\"c.lt.s\\t%0,%1\\t\\t# bge\", br_ops);
      output_asm_insn (\"bc1f\\t%2\\t\\t# bgeu (SF )Inv.\", br_ops);
    }
  else
    {
      output_asm_insn (\"bgeu\\t%0,%1,%2\\t\\t# bgeu Inv.\", br_ops);
    }
  return \" #\\tbgeu\\t%l0\\t\\t# bgeu\";
}
")

(define_insn ""
  [(set (pc)
	(if_then_else (gt (cc0)
			  (const_int 0))
		      (pc)
		      (label_ref (match_operand 0 "" ""))))]
  ""
  "*
{
  rtx br_ops[3];
  enum machine_mode mode;
  compare_restore (br_ops,  &mode, insn);
  br_ops[2] = operands[0];
  if (mode == DFmode)
    {
      output_asm_insn (\"c.le.d\\t%0,%1\\t\\t# ble\", br_ops);
      output_asm_insn (\"bc1t\\t%2\\t\\t# ble\", br_ops);
    }
  else if  (mode == SFmode)
    {
      output_asm_insn (\"c.le.s\\t%0,%1\\t\\t# ble\", br_ops);
      output_asm_insn (\"bc1t\\t%2\\t\\t# ble\", br_ops);
    }
  else
    {
      output_asm_insn (\"ble\\t%0,%1,%2\\t\\t# ble Inv.\", br_ops);
    }
  return \"\";
}
")

(define_insn ""
  [(set (pc)
	(if_then_else (gtu (cc0)
			   (const_int 0))
		      (pc)
		      (label_ref (match_operand 0 "" ""))))]
  ""
  "*
{
  rtx br_ops[3];
  enum machine_mode mode;
  compare_restore (br_ops,  &mode, insn);
  br_ops[2] = operands[0];
  if (mode == DFmode)
    {
      output_asm_insn (\"c.le.d\\t%0,%1\\t\\t# bleu\", br_ops);
      output_asm_insn (\"bc1t\\t%2\\t\\t# bleu\", br_ops);
    }
  else if  (mode == SFmode)
    {
      output_asm_insn (\"c.le.s\\t%0,%1\\t\\t# bleu\", br_ops);
      output_asm_insn (\"bc1t\\t%2\\t\\t# bleu\", br_ops);
    }
  else
    {
      output_asm_insn (\"bleu\\t%0,%1,%2\\t\\t# bleu Inv.\", br_ops);
    }
  return \"\";
}
")

;;
;;  ....................
;;
;;          LINKAGE
;;
;;  ....................

(define_insn "call"
  [(call (match_operand 0 "memory_operand" "m")
	 (match_operand 1 "" "i"))
   (clobber (reg:SI 31))]
  ""
  "*
{
  register rtx target = XEXP (operands[0], 0);

  if (GET_CODE (target) == SYMBOL_REF)
    return \"jal\\t%0\";

  else
    {
      operands[0] = target;
      return \"jal\\t$31,%0\";
    }
}")


(define_insn "call_value"
  [(set (match_operand 0 "" "=rf")
        (call (match_operand 1 "memory_operand" "m")
              (match_operand 2 "" "i")))
   (clobber (reg:SI 31))]
  ""
  "*
{
  register rtx target = XEXP (operands[1], 0);

  if (GET_CODE (target) == SYMBOL_REF)
    return \"jal\\t%1\";

  else
    {
      operands[1] = target;
      return \"jal\\t$31,%1\";
    }
}")

(define_insn "nop"
  [(const_int 0)]
  ""
  ".set\\tnoreorder\;nop\;.set\\treorder")

(define_insn "probe"
  [(mem:SI (reg:SI 29))]
  ""
  "*
{
  operands[0] = gen_rtx (REG, SImode, 1);
  operands[1] = stack_pointer_rtx;
  return \".set\\tnoat\;lw\\t%0,0(%1)\\t\\t# stack probe\;.set\\tat\";
}")

;;
;;- 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:
