;;- Machine description for SPUR chip for GNU C compiler
;;   Copyright (C) 1988 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.


;;- 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:

;; Compare instructions.
;; This pattern is used for generating an "insn"
;; which does just a compare and sets a (fictitious) condition code.

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

;; Put cmpsi first among compare insns so it matches two CONST_INT operands.

;; This controls RTL generation and register allocation.
(define_insn "cmpsi"
  [(set (cc0)
	(compare (match_operand:SI 0 "nonmemory_operand" "rK")
		 (match_operand:SI 1 "nonmemory_operand" "rK")))]
  ""
  "*
{
  cc_status.value1 = operands[0], cc_status.value2 = operands[1];
  return \"\";
}")

;; Put tstsi first among test insns so it matches a CONST_INT operand.

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

(define_insn "tstsi"
  [(set (cc0)
	(match_operand:SI 0 "register_operand" "r"))]
  ""
  "*
{
  cc_status.value1 = operands[0], cc_status.value2 = const0_rtx;
  return \"\";
}")


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

(define_insn "beq"
  [(set (pc)
	(if_then_else (eq (cc0)
			  (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "* return output_compare (operands, \"eq\", \"eq\", \"ne\", \"ne\"); ")

(define_insn "bne"
  [(set (pc)
	(if_then_else (ne (cc0)
			  (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "* return output_compare (operands, \"ne\", \"ne\", \"eq\", \"eq\"); ")

(define_insn "bgt"
  [(set (pc)
	(if_then_else (gt (cc0)
			  (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "* return output_compare (operands, \"gt\", \"lt\", \"le\", \"ge\"); ")

(define_insn "bgtu"
  [(set (pc)
	(if_then_else (gtu (cc0)
			   (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "* return output_compare (operands, \"ugt\", \"ult\", \"ule\", \"uge\"); ")

(define_insn "blt"
  [(set (pc)
	(if_then_else (lt (cc0)
			  (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "* return output_compare (operands, \"lt\", \"gt\", \"ge\", \"le\"); ")

(define_insn "bltu"
  [(set (pc)
	(if_then_else (ltu (cc0)
			   (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "* return output_compare (operands, \"ult\", \"ugt\", \"uge\", \"ule\"); ")

(define_insn "bge"
  [(set (pc)
	(if_then_else (ge (cc0)
			  (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "* return output_compare (operands, \"ge\", \"le\", \"lt\", \"gt\"); ")

(define_insn "bgeu"
  [(set (pc)
	(if_then_else (geu (cc0)
			   (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "* return output_compare (operands, \"uge\", \"ule\", \"ult\", \"ugt\"); ")

(define_insn "ble"
  [(set (pc)
	(if_then_else (le (cc0)
			  (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "* return output_compare (operands, \"le\", \"ge\", \"gt\", \"lt\"); ")

(define_insn "bleu"
  [(set (pc)
	(if_then_else (leu (cc0)
			   (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "* return output_compare (operands, \"ule\", \"uge\", \"ugt\", \"ult\"); ")

;; These match inverted jump insns for register allocation.

(define_insn ""
  [(set (pc)
	(if_then_else (eq (cc0)
			  (const_int 0))
		      (pc)
		      (label_ref (match_operand 0 "" ""))))]
  ""
  "* return output_compare (operands, \"ne\", \"ne\", \"eq\", \"eq\"); ")

(define_insn ""
  [(set (pc)
	(if_then_else (ne (cc0)
			  (const_int 0))
		      (pc)
		      (label_ref (match_operand 0 "" ""))))]
  ""
  "* return output_compare (operands, \"eq\", \"eq\", \"ne\", \"ne\"); ")

(define_insn ""
  [(set (pc)
	(if_then_else (gt (cc0)
			  (const_int 0))
		      (pc)
		      (label_ref (match_operand 0 "" ""))))]
  ""
  "* return output_compare (operands, \"le\", \"ge\", \"gt\", \"lt\"); ")

(define_insn ""
  [(set (pc)
	(if_then_else (gtu (cc0)
			   (const_int 0))
		      (pc)
		      (label_ref (match_operand 0 "" ""))))]
  ""
  "* return output_compare (operands, \"ule\", \"uge\", \"ugt\", \"ult\"); ")

(define_insn ""
  [(set (pc)
	(if_then_else (lt (cc0)
			  (const_int 0))
		      (pc)
		      (label_ref (match_operand 0 "" ""))))]
  ""
  "* return output_compare (operands, \"ge\", \"le\", \"lt\", \"gt\"); ")

(define_insn ""
  [(set (pc)
	(if_then_else (ltu (cc0)
			   (const_int 0))
		      (pc)
		      (label_ref (match_operand 0 "" ""))))]
  ""
  "* return output_compare (operands, \"uge\", \"ule\", \"ult\", \"ugt\"); ")

(define_insn ""
  [(set (pc)
	(if_then_else (ge (cc0)
			  (const_int 0))
		      (pc)
		      (label_ref (match_operand 0 "" ""))))]
  ""
  "* return output_compare (operands, \"lt\", \"gt\", \"ge\", \"le\"); ")

(define_insn ""
  [(set (pc)
	(if_then_else (geu (cc0)
			   (const_int 0))
		      (pc)
		      (label_ref (match_operand 0 "" ""))))]
  ""
  "* return output_compare (operands, \"ult\", \"ugt\", \"uge\", \"ule\"); ")

(define_insn ""
  [(set (pc)
	(if_then_else (le (cc0)
			  (const_int 0))
		      (pc)
		      (label_ref (match_operand 0 "" ""))))]
  ""
  "* return output_compare (operands, \"gt\", \"lt\", \"le\", \"ge\"); ")

(define_insn ""
  [(set (pc)
	(if_then_else (leu (cc0)
			   (const_int 0))
		      (pc)
		      (label_ref (match_operand 0 "" ""))))]
  ""
  "* return output_compare (operands, \"ugt\", \"ult\", \"ule\", \"uge\"); ")

;; Move instructions

(define_insn "movsi"
  [(set (match_operand:SI 0 "general_operand" "=r,m")
	(match_operand:SI 1 "general_operand" "rmi,rJ"))]
  ""
  "*
{
  if (GET_CODE (operands[0]) == MEM)
    return \"st_32 %r1,%0\";
  if (GET_CODE (operands[1]) == MEM)
    return \"ld_32 %0,%1\;nop\";
  if (GET_CODE (operands[1]) == REG)
    return \"add_nt %0,%1,$0\";
  if (GET_CODE (operands[1]) == SYMBOL_REF && operands[1]->unchanging)
    return \"add_nt %0,r24,$(%1-0b)\";
  return \"add_nt %0,r0,%1\";
}")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r")
	(mem:SI (plus:SI (match_operand:SI 1 "register_operand" "r")
			 (match_operand:SI 2 "register_operand" "r"))))]
  ""
  "ld_32 %0,%1,%2\;nop")

;; Generate insns for moving single bytes.

(define_expand "movqi"
  [(set (match_operand:QI 0 "general_operand" "")
	(match_operand:QI 1 "general_operand" ""))]
  ""
  "
{
  if (GET_CODE (operands[0]) == MEM && GET_CODE (operands[1]) == MEM)
    operands[1] = copy_to_reg (operands[1]);

  if (GET_CODE (operands[1]) == MEM)
    {
      rtx tem = gen_reg_rtx (SImode);
      rtx addr = force_reg (SImode, XEXP (operands[1], 0));
      rtx subreg;

      emit_move_insn (tem, gen_rtx (MEM, SImode, addr));
      if (GET_CODE (operands[0]) == SUBREG)
	subreg = gen_rtx (SUBREG, SImode, SUBREG_REG (operands[0]),
			  SUBREG_WORD (operands[0]));
      else
	subreg = gen_rtx (SUBREG, SImode, operands[0], 0);

      emit_insn (gen_rtx (SET, VOIDmode, subreg,
			  gen_rtx (ZERO_EXTRACT, SImode, tem,
				   gen_rtx (CONST_INT, VOIDmode, 8),
				   addr)));
    }
  else if (GET_CODE (operands[0]) == MEM)
    {
      rtx tem = gen_reg_rtx (SImode);
      rtx addr = force_reg (SImode, XEXP (operands[0], 0));
      rtx subreg;

      emit_move_insn (tem, gen_rtx (MEM, SImode, addr));
      if (! CONSTANT_ADDRESS_P (operands[1]))
	{
	  if (GET_CODE (operands[1]) == SUBREG)
	    subreg = gen_rtx (SUBREG, SImode, SUBREG_REG (operands[1]),
			      SUBREG_WORD (operands[1]));
	  else
	    subreg = gen_rtx (SUBREG, SImode, operands[1], 0);
	}

      emit_insn (gen_rtx (SET, VOIDmode,
			  gen_rtx (ZERO_EXTRACT, SImode, tem,
				   gen_rtx (CONST_INT, VOIDmode, 8),
				   addr),
			  subreg));
      emit_move_insn (gen_rtx (MEM, SImode, addr), tem);
    }
  else
    {
      emit_insn (gen_rtx (SET, VOIDmode, operands[0], operands[1]));
    }
  DONE;
}")

;; Recognize insns generated for moving single bytes.

(define_insn ""
  [(set (match_operand:QI 0 "general_operand" "=r,m")
	(match_operand:QI 1 "general_operand" "rmi,r"))]
  ""
  "*
{
  if (GET_CODE (operands[0]) == MEM)
    return \"st_32 %1,%0\";
  if (GET_CODE (operands[1]) == MEM)
    return \"ld_32 %0,%1\;nop\";
  if (GET_CODE (operands[1]) == REG)
    return \"add_nt %0,%1,$0\";
  return \"add_nt %0,r0,%1\";
}")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r")
	(zero_extract:SI (match_operand:SI 1 "register_operand" "r")
			 (const_int 8)
			 (match_operand:SI 2 "nonmemory_operand" "rI")))]
  ""
  "extract %0,%1,%2")

(define_insn ""
  [(set (zero_extract:SI (match_operand:SI 0 "register_operand" "+r")
			 (const_int 8)
			 (match_operand:SI 1 "nonmemory_operand" "rI"))
	(match_operand:SI 2 "nonmemory_operand" "ri"))]
  ""
  "wr_insert %1\;insert %0,%0,%2")

;; Constant propagation can optimize the previous pattern into this pattern.
;[Not any more.  It could when the position-operand contains a MULT.]

;(define_insn ""
;  [(set (zero_extract:QI (match_operand:SI 0 "register_operand" "+r")
;			 (const_int 8)
;			 (match_operand:SI 1 "immediate_operand" "I"))
;	(match_operand:QI 2 "register_operand" "r"))]
;  "GET_CODE (operands[1]) == CONST_INT
;   && INTVAL (operands[1]) % 8 == 0
;   && (unsigned) INTVAL (operands[1]) < 32"
;  "*
;{
;  operands[1] = gen_rtx (CONST_INT, VOIDmode, INTVAL (operands[1]) / 8);
;  return \"wr_insert 0,0,%1\;insert %0,%0,%2\";
;}")

;; The three define_expand patterns on this page
;; serve as subroutines of "movhi".

;; Generate code to fetch an aligned halfword from memory.
;; Operand 0 is the destination register (HImode).
;; Operand 1 is the memory address (SImode).
;; Operand 2 is a temporary (SImode).
;; Operand 3 is a temporary (SImode).
;; Operand 4 is a temporary (QImode).

;; Operand 5 is an internal temporary (HImode).

(define_expand "loadhi"
  [(set (match_operand:SI 2 "register_operand" "")
	(mem:SI (match_operand:SI 1 "register_operand" "")))
   ;; Extract the low byte.
   (set (subreg:SI (match_dup 5) 0)
	(zero_extract:SI (match_dup 2) (const_int 8) (match_dup 1)))
   ;; Form address of high byte.
   (set (match_operand:SI 3 "register_operand" "")
	(plus:SI (match_dup 1) (const_int 1)))
   ;; Extract the high byte.
   (set (subreg:SI (match_operand:QI 4 "register_operand" "") 0)
	(zero_extract:SI (match_dup 2) (const_int 8) (match_dup 3)))
   ;; Put the high byte in with the low one.
   (set (zero_extract:SI (match_dup 5) (const_int 8) (const_int 1))
	(subreg:SI (match_dup 4) 0))
   (set (match_operand:HI 0 "register_operand" "") (match_dup 5))]
  ""
  "operands[5] = gen_reg_rtx (HImode);")

;; Generate code to store an aligned halfword into memory.
;; Operand 0 is the destination address (SImode).
;; Operand 1 is the source register (HImode, not constant).
;; Operand 2 is a temporary (SImode).
;; Operand 3 is a temporary (SImode).
;; Operand 4 is a temporary (QImode).

;; Operand 5 is an internal variable made from operand 1.

(define_expand "storehi"
  [(set (match_operand:SI 2 "register_operand" "")
	(mem:SI (match_operand:SI 0 "register_operand" "")))
   ;; Insert the low byte.
   (set (zero_extract:SI (match_dup 2) (const_int 8) (match_dup 0))
	(match_dup 5))
   ;; Form address of high byte.
   (set (match_operand:SI 3 "register_operand" "")
	(plus:SI (match_dup 0) (const_int 1)))
   ;; Extract the high byte from the source.
   (set (subreg:SI (match_operand:QI 4 "register_operand" "") 0)
	(zero_extract:SI (match_operand:HI 1 "register_operand" "")
			 (const_int 8) (const_int 1)))
   ;; Store high byte into the memory word
   (set (zero_extract:SI (match_dup 2) (const_int 8) (match_dup 3))
	(subreg:SI (match_dup 4) 0))
   ;; Put memory word back into memory.
   (set (mem:SI (match_dup 0))
	(match_dup 2))]
  ""
  "
{
  if (GET_CODE (operands[1]) == SUBREG)
    operands[5] = gen_rtx (SUBREG, SImode, SUBREG_REG (operands[1]),
			   SUBREG_WORD (operands[1]));
  else
    operands[5] = gen_rtx (SUBREG, SImode, operands[1], 0);
}")

;; Like storehi but operands[1] is a CONST_INT.

(define_expand "storeinthi"
  [(set (match_operand:SI 2 "register_operand" "")
	(mem:SI (match_operand:SI 0 "register_operand" "")))
   ;; Insert the low byte.
   (set (zero_extract:SI (match_dup 2) (const_int 8) (match_dup 0))
	(match_dup 5))
   ;; Form address of high byte.
   (set (match_operand:SI 3 "register_operand" "")
	(plus:SI (match_dup 0) (const_int 1)))
   ;; Store high byte into the memory word
   (set (zero_extract:SI (match_dup 2) (const_int 8) (match_dup 3))
	(match_dup 6))
   ;; Put memory word back into memory.
   (set (mem:SI (match_dup 0))
	(match_dup 2))]
  ""
  " operands[5] = gen_rtx (CONST_INT, VOIDmode, INTVAL (operands[1]) & 255);
    operands[6] = gen_rtx (CONST_INT, VOIDmode,
			   (INTVAL (operands[1]) >> 8) & 255);
")

;; Main entry for generating insns to move halfwords.

(define_expand "movhi"
  [(set (match_operand:HI 0 "general_operand" "")
	(match_operand:HI 1 "general_operand" ""))]
  ""
  "
{
  if (GET_CODE (operands[0]) == MEM && GET_CODE (operands[1]) == MEM)
    operands[1] = copy_to_reg (operands[1]);
  
  if (GET_CODE (operands[1]) == MEM)
    {
      rtx insn =
	emit_insn (gen_loadhi (operands[0],
			       force_reg (SImode, XEXP (operands[1], 0)),
			       gen_reg_rtx (SImode), gen_reg_rtx (SImode),
			       gen_reg_rtx (QImode)));
      /* Tell cse what value the loadhi produces, so it detect duplicates.  */
      REG_NOTES (insn) = gen_rtx (EXPR_LIST, REG_EQUAL, operands[1],
					     REG_NOTES (insn));
    }
  else if (GET_CODE (operands[0]) == MEM)
    {
      if (GET_CODE (operands[1]) == CONST_INT)
	emit_insn (gen_storeinthi (force_reg (SImode, XEXP (operands[0], 0)),
			           operands[1],
				   gen_reg_rtx (SImode), gen_reg_rtx (SImode),
				   gen_reg_rtx (QImode)));
      else
	{
	  if (CONSTANT_P (operands[1]))
            operands[1] = force_reg (HImode, operands[1]);
	  emit_insn (gen_storehi (force_reg (SImode, XEXP (operands[0], 0)),
				  operands[1],
				  gen_reg_rtx (SImode), gen_reg_rtx (SImode),
				  gen_reg_rtx (QImode)));
	}
    }
  else
    emit_insn (gen_rtx (SET, VOIDmode, operands[0], operands[1]));
  DONE;
}")

;; Recognize insns generated for moving halfwords.
;; (Note that the extract and insert patterns for single-byte moves
;;  are also involved in recognizing some of the insns used for this purpose.)

(define_insn ""
  [(set (match_operand:HI 0 "general_operand" "=r,m")
	(match_operand:HI 1 "general_operand" "rmi,r"))]
  ""
  "*
{
  if (GET_CODE (operands[0]) == MEM)
    return \"st_32 %1,%0\";
  if (GET_CODE (operands[1]) == MEM)
    return \"ld_32 %0,%1\;nop\";
  if (GET_CODE (operands[1]) == REG)
    return \"add_nt %0,%1,$0\";
  return \"add_nt %0,r0,%1\";
}")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r")
	(zero_extract:SI (match_operand:HI 1 "register_operand" "r")
			 (const_int 8)
			 (match_operand:SI 2 "nonmemory_operand" "rI")))]
  ""
  "extract %0,%1,%2")

(define_insn ""
  [(set (zero_extract:SI (match_operand:HI 0 "register_operand" "+r")
			 (const_int 8)
			 (match_operand:SI 1 "nonmemory_operand" "rI"))
	(match_operand:SI 2 "nonmemory_operand" "ri"))]
  ""
  "wr_insert %1\;insert %0,%0,%2")

;; Constant propagation can optimize the previous pattern into this pattern.

;(define_insn ""
;  [(set (zero_extract:QI (match_operand:HI 0 "register_operand" "+r")
;			 (const_int 8)
;			 (match_operand:SI 1 "immediate_operand" "I"))
;	(match_operand:QI 2 "register_operand" "r"))]
;  "GET_CODE (operands[1]) == CONST_INT
;   && INTVAL (operands[1]) % 8 == 0
;   && (unsigned) INTVAL (operands[1]) < 32"
;  "*
;{
;  operands[1] = gen_rtx (CONST_INT, VOIDmode, INTVAL (operands[1]) / 8);
;  return \"wr_insert 0,0,%1\;insert %0,%0,%2\";
;}")

;; 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]))
    return output_fp_move_double (operands);
  if (operands[1] == dconst0_rtx && GET_CODE (operands[0]) == REG)
    {
      operands[1] = gen_rtx (REG, SImode, REGNO (operands[0]) + 1);
      return \"add_nt %0,r0,$0\;add_nt %1,r0,$0\";
    }
  if (operands[1] == dconst0_rtx && GET_CODE (operands[0]) == MEM)
    {
      operands[1] = adj_offsettable_operand (operands[0], 4);
      return \"st_32 r0,%0\;st_32 r0,%1\";
    }
  return output_move_double (operands);
}
")
  
(define_insn "movdf"
  [(set (match_operand:DF 0 "general_operand" "=r,&r,m,?f,?rm")
	(match_operand:DF 1 "general_operand" "r,m,r,rfm,f"))]
  ""
  "*
{
  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" "=r,&r,m,?f,?rm")
	(match_operand:DI 1 "general_operand" "r,m,r,rfm,f"))]
  ""
  "*
{
  if (FP_REG_P (operands[0]) || FP_REG_P (operands[1]))
    return output_fp_move_double (operands);
  return output_move_double (operands);
}
")

(define_insn "movsf"
  [(set (match_operand:SF 0 "general_operand" "=rf,m")
	(match_operand:SF 1 "general_operand" "rfm,rf"))]
  ""
  "*
{
  if (FP_REG_P (operands[0]))
    {
      if (FP_REG_P (operands[1]))
	return \"fmov %0,%1\";
      if (GET_CODE (operands[1]) == REG)
	{
	  rtx xoperands[2];
	  int offset = - get_frame_size () - 8;
	  xoperands[1] = operands[1];
	  xoperands[0] = gen_rtx (CONST_INT, VOIDmode, offset);
	  output_asm_insn (\"st_32 %1,r25,%0\", xoperands);
	  xoperands[1] = operands[0];
	  output_asm_insn (\"ld_sgl %1,r25,%0\;nop\", xoperands);
	  return \"\";
	}
      return \"ld_sgl %0,%1\;nop\";
    }
  if (FP_REG_P (operands[1]))
    {
      if (GET_CODE (operands[0]) == REG)
	{
	  rtx xoperands[2];
	  int offset = - get_frame_size () - 8;
	  xoperands[0] = gen_rtx (CONST_INT, VOIDmode, offset);
	  xoperands[1] = operands[1];
	  output_asm_insn (\"st_sgl %1,r25,%0\", xoperands);
	  xoperands[1] = operands[0];
	  output_asm_insn (\"ld_32 %1,r25,%0\;nop\", xoperands);
	  return \"\";
	}
      return \"st_sgl %1,%0\";
    }
  if (GET_CODE (operands[0]) == MEM)
    return \"st_32 %r1,%0\";
  if (GET_CODE (operands[1]) == MEM)
    return \"ld_32 %0,%1\;nop\";
  if (GET_CODE (operands[1]) == REG)
    return \"add_nt %0,%1,$0\";
  return \"add_nt %0,r0,%1\";
}")

;;- truncation instructions
(define_insn "truncsiqi2"
  [(set (match_operand:QI 0 "register_operand" "=r")
	(truncate:QI
	 (match_operand:SI 1 "register_operand" "r")))]
  ""
  "add_nt %0,%1,$0")

(define_insn "trunchiqi2"
  [(set (match_operand:QI 0 "register_operand" "=r")
	(truncate:QI
	 (match_operand:HI 1 "register_operand" "r")))]
  ""
  "add_nt %0,%1,$0")

(define_insn "truncsihi2"
  [(set (match_operand:HI 0 "register_operand" "=r")
	(truncate:HI
	 (match_operand:SI 1 "register_operand" "r")))]
  ""
  "add_nt %0,%1,$0")

;;- zero extension instructions

;; Note that the one starting from HImode comes before those for QImode
;; so that a constant operand will match HImode, not QImode.
(define_expand "zero_extendhisi2"
  [(set (match_operand:SI 0 "register_operand" "")
	(and:SI (match_operand:HI 1 "register_operand" "") ;Changed to SI below
		;; This constant is invalid, but reloading will handle it.
		;; It's useless to generate here the insns to construct it
		;; because constant propagation would simplify them anyway.
		(match_dup 2)))]
  ""
  "
{
  if (GET_CODE (operands[1]) == SUBREG)
    operands[1] = gen_rtx (SUBREG, SImode, SUBREG_REG (operands[1]),
			   SUBREG_WORD (operands[1]));
  else
    operands[1] = gen_rtx (SUBREG, SImode, operands[1], 0);

  operands[2] = force_reg (SImode, gen_rtx (CONST_INT, VOIDmode, 65535));
}")

(define_insn "zero_extendqihi2"
  [(set (match_operand:HI 0 "register_operand" "=r")
	(zero_extend:HI
	 (match_operand:QI 1 "register_operand" "r")))]
  ""
  "extract %0,%1,$0")

(define_insn "zero_extendqisi2"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(zero_extend:SI
	 (match_operand:QI 1 "register_operand" "r")))]
  ""
  "extract %0,%1,$0")

;;- sign extension instructions
;; Note that the one starting from HImode comes before those for QImode
;; so that a constant operand will match HImode, not QImode.

(define_expand "extendhisi2"
  [(set (match_dup 2)
	(and:SI (match_operand:HI 1 "register_operand" "") ;Changed to SI below
		(match_dup 4)))
   (set (match_dup 3) (plus:SI (match_dup 2) (match_dup 5)))
   (set (match_operand:SI 0 "register_operand" "")
	(xor:SI (match_dup 3) (match_dup 5)))]
  ""
  "
{
  if (GET_CODE (operands[1]) == SUBREG)
    operands[1] = gen_rtx (SUBREG, SImode, SUBREG_REG (operands[1]),
			   SUBREG_WORD (operands[1]));
  else
    operands[1] = gen_rtx (SUBREG, SImode, operands[1], 0);

  operands[2] = gen_reg_rtx (SImode);
  operands[3] = gen_reg_rtx (SImode);
  operands[4] = force_reg (SImode, gen_rtx (CONST_INT, VOIDmode, 65535));
  operands[5] = force_reg (SImode, gen_rtx (CONST_INT, VOIDmode, -32768));
}")

(define_expand "extendqihi2"
  [(set (match_dup 2)
	(and:HI (match_operand:QI 1 "register_operand" "") ;Changed to SI below
		(const_int 255)))
   (set (match_dup 3)
	(plus:SI (match_dup 2) (const_int -128)))
   (set (match_operand:HI 0 "register_operand" "")
	(xor:SI (match_dup 3) (const_int -128)))]
  ""
  "
{
  if (GET_CODE (operands[1]) == SUBREG)
    operands[1] = gen_rtx (SUBREG, HImode, SUBREG_REG (operands[1]),
			   SUBREG_WORD (operands[1]));
  else
    operands[1] = gen_rtx (SUBREG, HImode, operands[1], 0);

  operands[2] = gen_reg_rtx (HImode);
  operands[3] = gen_reg_rtx (HImode);
}")

(define_expand "extendqisi2"
  [(set (match_dup 2)
	(and:SI (match_operand:QI 1 "register_operand" "") ;Changed to SI below
		(const_int 255)))
   (set (match_dup 3) (plus:SI (match_dup 2) (const_int -128)))
   (set (match_operand:SI 0 "register_operand" "")
	(xor:SI (match_dup 3) (const_int -128)))]
  ""
  "
{
  if (GET_CODE (operands[1]) == SUBREG)
    operands[1] = gen_rtx (SUBREG, SImode, SUBREG_REG (operands[1]),
			   SUBREG_WORD (operands[1]));
  else
    operands[1] = gen_rtx (SUBREG, SImode, operands[1], 0);

  operands[2] = gen_reg_rtx (SImode);
  operands[3] = gen_reg_rtx (SImode);
}")

;;- arithmetic instructions

(define_insn "addsi3"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(plus:SI (match_operand:SI 1 "nonmemory_operand" "%r")
		 (match_operand:SI 2 "nonmemory_operand" "rI")))]
  ""
  "add %0,%1,%2")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r")
	(plus:SI (match_operand:SI 1 "nonmemory_operand" "%r")
		 (match_operand:SI 2 "big_immediate_operand" "g")))]
  "GET_CODE (operands[2]) == CONST_INT 
   && (unsigned) (INTVAL (operands[2]) + 0x8000000) < 0x10000000"
  "*
{
  return 
    output_add_large_offset (operands[0], operands[1], INTVAL (operands[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 "nonmemory_operand" "rI")))]
  ""
  "sub %0,%1,%2")

(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" "rI")))]
  ""
  "and %0,%1,%2")

(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" "rI")))]
  ""
  "or %0,%1,%2")

(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" "rI")))]
  ""
  "xor %0,%1,%2")

(define_insn "negsi2"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(neg:SI (match_operand:SI 1 "nonmemory_operand" "rI")))]
  ""
  "sub %0,r0,%1")

(define_insn "one_cmplsi2"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(not:SI (match_operand:SI 1 "register_operand" "r")))]
  ""
  "xor %0,%1,$-1")

;; 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")))]
  "TARGET_FPU"
  "fadd %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")))]
  "TARGET_FPU"
  "fadd %0,%1,%2")

(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")))]
  "TARGET_FPU"
  "fsub %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")))]
  "TARGET_FPU"
  "fsub %0,%1,%2")

(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")))]
  "TARGET_FPU"
  "fmul %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")))]
  "TARGET_FPU"
  "fmul %0,%1,%2")

(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")))]
  "TARGET_FPU"
  "fdiv %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")))]
  "TARGET_FPU"
  "fdiv %0,%1,%2")

(define_insn "negdf2"
  [(set (match_operand:DF 0 "register_operand" "=f")
	(neg:DF (match_operand:DF 1 "nonmemory_operand" "f")))]
  "TARGET_FPU"
  "fneg %0,%1")

(define_insn "negsf2"
  [(set (match_operand:SF 0 "register_operand" "=f")
	(neg:SF (match_operand:SF 1 "nonmemory_operand" "f")))]
  "TARGET_FPU"
  "fneg %0,%1")

(define_insn "absdf2"
  [(set (match_operand:DF 0 "register_operand" "=f")
	(abs:DF (match_operand:DF 1 "nonmemory_operand" "f")))]
  "TARGET_FPU"
  "fabs %0,%1")

(define_insn "abssf2"
  [(set (match_operand:SF 0 "register_operand" "=f")
	(abs:SF (match_operand:SF 1 "nonmemory_operand" "f")))]
  "TARGET_FPU"
  "fabs %0,%1")

;; Shift instructions

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r")
	(ashift:SI (match_operand:SI 1 "register_operand" "r")
		   (match_operand:SI 2 "immediate_operand" "I")))]
  "GET_CODE (operands[2]) == CONST_INT"
  "*
{
  unsigned int amount = INTVAL (operands[2]);

  switch (amount)
    {
    case 0:
      return \"add_nt %0,%1,$0\";
    case 1:
      return \"sll %0,%1,$1\";
    case 2:
      return \"sll %0,%1,$2\";
    default:
      output_asm_insn (\"sll %0,%1,$3\", operands);

      for (amount -= 3; amount >= 3; amount -= 3)
	output_asm_insn (\"sll %0,%0,$3\", operands);

      if (amount > 0)
	output_asm_insn (amount == 1 ? \"sll %0,%0,$1\" : \"sll %0,%0,$2\",
			 operands);
      return \"\";
    }
}")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r")
	(ashiftrt:SI (match_operand:SI 1 "register_operand" "r")
	             (match_operand:SI 2 "immediate_operand" "I")))]
  "GET_CODE (operands[2]) == CONST_INT"
  "*
{
  unsigned int amount = INTVAL (operands[2]);

  if (amount == 0) 
    return \"add_nt %0,%1,$0\";
  else
    output_asm_insn (\"sra %0,%1,$1\", operands);
  
  for (amount -= 1; amount > 0; amount -= 1)
    output_asm_insn (\"sra %0,%0,$1\", operands);

  return \"\";
}")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r")
	(lshiftrt:SI (match_operand:SI 1 "register_operand" "r")
	             (match_operand:SI 2 "immediate_operand" "I")))]
  "GET_CODE (operands[2]) == CONST_INT"
  "*
{
  unsigned int amount = INTVAL (operands[2]);

  if (amount == 0) 
    return \"add_nt %0,%1,$0\";
  else
    output_asm_insn (\"srl %0,%1,$1\", operands);
  
  for (amount -= 1; amount > 0; amount -= 1)
    output_asm_insn (\"srl %0,%0,$1\", operands);

  return \"\";
}")

(define_expand "ashlsi3"
  [(set (match_operand:SI 0 "register_operand" "")
	(ashift:SI (match_operand:SI 1 "register_operand" "")
		   (match_operand:SI 2 "nonmemory_operand" "")))]
  ""
  "
{
  if (GET_CODE (operands[2]) != CONST_INT
      || (! TARGET_EXPAND_SHIFTS && (unsigned) INTVAL (operands[2]) > 3))
    FAIL;
}")

(define_expand "lshlsi3"
  [(set (match_operand:SI 0 "register_operand" "")
	(ashift:SI (match_operand:SI 1 "register_operand" "")
		   (match_operand:SI 2 "nonmemory_operand" "")))]
  ""
  "
{
  if (GET_CODE (operands[2]) != CONST_INT
      || (! TARGET_EXPAND_SHIFTS && (unsigned) INTVAL (operands[2]) > 3))
    FAIL;
}")

(define_expand "ashrsi3"
  [(set (match_operand:SI 0 "register_operand" "")
	(ashiftrt:SI (match_operand:SI 1 "register_operand" "")
		     (match_operand:SI 2 "nonmemory_operand" "")))]
  ""
  "
{
  if (GET_CODE (operands[2]) != CONST_INT
      || (! TARGET_EXPAND_SHIFTS && (unsigned) INTVAL (operands[2]) > 1))
    FAIL;
}")

(define_expand "lshrsi3"
  [(set (match_operand:SI 0 "register_operand" "")
	(lshiftrt:SI (match_operand:SI 1 "register_operand" "")
		     (match_operand:SI 2 "nonmemory_operand" "")))]
  ""
  "
{
  if (GET_CODE (operands[2]) != CONST_INT
      || (! TARGET_EXPAND_SHIFTS && (unsigned) INTVAL (operands[2]) > 1))
    FAIL;
}")

;; Unconditional and other jump instructions
(define_insn "jump"
  [(set (pc)
	(label_ref (match_operand 0 "" "")))]
  ""
  "jump %l0\;nop")

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

;;- jump to subroutine
(define_insn "call"
  [(call (match_operand:SI 0 "memory_operand" "m")
	 (match_operand:SI 1 "general_operand" "g"))]
  ;;- Don't use operand 1 for most machines.
  ""
  "add_nt r2,%0\;call .+8\;jump_reg r0,r2\;nop")

(define_insn "call_value"
  [(set (match_operand 0 "" "=g")
	(call (match_operand:SI 1 "memory_operand" "m")
	      (match_operand:SI 2 "general_operand" "g")))]
  ;;- Don't use operand 1 for most machines.
  ""
  "add_nt r2,%1\;call .+8\;jump_reg r0,r2\;nop")

;; A memory ref with constant address is not normally valid.
;; But it is valid in a call insns.  This pattern allows the
;; loading of the address to combine with the call.
(define_insn ""
  [(call (mem:SI (match_operand:SI 0 "" "i"))
	 (match_operand:SI 1 "general_operand" "g"))]
  ;;- Don't use operand 1 for most machines.
  "GET_CODE (operands[0]) == SYMBOL_REF"
  "call %0\;nop")

(define_insn ""
  [(set (match_operand 0 "" "=g")
	(call (mem:SI (match_operand:SI 1 "" "i"))
	      (match_operand:SI 2 "general_operand" "g")))]
  ;;- Don't use operand 1 for most machines.
  "GET_CODE (operands[1]) == SYMBOL_REF"
  "call %1\;nop")

(define_insn "nop"
  [(const_int 0)]
  ""
  "nop")

;;- 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:

