;; GCC machine description for Matsushita MN10300
;; Copyright (C) 1996 Free Software Foundation, Inc.

;;   Contributed by Jeff Law (law@cygnus.com).

;; This file is part of GNU CC.

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

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

;; You should have received a copy of the GNU General Public License
;; along with GNU CC; see the file COPYING.  If not, write to
;; the Free Software Foundation, 59 Temple Place - Suite 330,
;; Boston, MA 02111-1307, USA.

;; The original PO technology requires these to be ordered by speed,
;; so that assigner will pick the fastest.

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

;; Condition code settings.
;; none - insn does not affect cc
;; none_0hit - insn does not affect cc but it does modify operand 0
;;	This attribute is used to keep track of when operand 0 changes.
;; 	See the description of NOTICE_UPDATE_CC for more info.
;; set - insn sets flags z,n.  v is unusable c is set to 0.
;;	(c may not really be set to 0 but that's ok, we don't need it anyway).
;; set_zn_c0 - insn sets z,n to usable values.  v is unknown.  c may or may not
;;	be known (if it isn't that's ok, we don't need it anyway).
;; compare - compare instruction
;; clobber - value of cc is unknown
(define_attr "cc" "none,none_0hit,set,set_zn_c0,compare,clobber"
  (const_string "clobber"))

;; ----------------------------------------------------------------------
;; MOVE INSTRUCTIONS
;; ----------------------------------------------------------------------

;; movqi

(define_expand "movqi"
  [(set (match_operand:QI 0 "general_operand" "")
	(match_operand:QI 1 "general_operand" ""))]
  ""
  "
{
  /* One of the ops has to be in a register */
  if (!register_operand (operand0, QImode)
      && !register_operand (operand1, QImode))
    operands[1] = copy_to_mode_reg (QImode, operand1);
}")

(define_insn ""
  [(set (match_operand:QI 0 "general_operand" "=d,a,d,d,a,d,a,d,m")
	(match_operand:QI 1 "general_operand" "0,0,I,a,d,di,ia,m,d"))]
  "register_operand (operands[0], QImode)
   || register_operand (operands[1], QImode)"
  "@
  nop
  nop
  clr %0
  mov %1,%0
  mov %1,%0
  mov %1,%0
  mov %1,%0
  movbu %1,%0
  movbu %1,%0"
  [(set_attr "cc" "none,none,clobber,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit")])

;; movhi

(define_expand "movhi"
  [(set (match_operand:HI 0 "general_operand" "")
	(match_operand:HI 1 "general_operand" ""))]
  ""
  "
{
  /* One of the ops has to be in a register */
  if (!register_operand (operand1, HImode)
      && !register_operand (operand0, HImode))
    operands[1] = copy_to_mode_reg (HImode, operand1);
}")

(define_insn ""
  [(set (match_operand:HI 0 "general_operand" "=d,a,d,d,a,d,a,d,m")
	(match_operand:HI 1 "general_operand" "0,0,I,a,d,di,ia,m,d"))]
  "register_operand (operands[0], HImode)
   || register_operand (operands[1], HImode)"
  "@
  nop
  nop
  clr %0
  mov %1,%0
  mov %1,%0
  mov %1,%0
  mov %1,%0
  movhu %1,%0
  movhu %1,%0"
  [(set_attr "cc" "none,none,clobber,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit")])

;; movsi and helpers

(define_expand "movsi"
  [(set (match_operand:SI 0 "general_operand" "")
	(match_operand:SI 1 "general_operand" ""))]
  ""
  "
{
  /* One of the ops has to be in a register */
  if (!register_operand (operand1, SImode)
      && !register_operand (operand0, SImode))
    operands[1] = copy_to_mode_reg (SImode, operand1);
}")

;; We could improve loading of some constants with a little work.
(define_insn ""
  [(set (match_operand:SI 0 "general_operand" "=d,a,d,dm,dm,am,am,d,d,a,a,a,x")
	(match_operand:SI 1 "general_operand" "0,0,I,d,a,d,a,dim,aim,dim,aim,x,a"))]
  "register_operand (operands[0], SImode)
   || register_operand (operands[1], SImode)"
  "@
  nop
  nop
  clr %0
  mov %1,%0
  mov %1,%0
  mov %1,%0
  mov %1,%0
  mov %1,%0
  mov %1,%0
  mov %1,%0
  mov %1,%0
  mov %1,%0
  mov %1,%0"
  [(set_attr "cc" "none,none,clobber,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit")])

(define_expand "movsf"
  [(set (match_operand:SF 0 "general_operand" "")
	(match_operand:SF 1 "general_operand" ""))]
  ""
  "
{
  /* One of the ops has to be in a register */
  if (!register_operand (operand1, SFmode)
      && !register_operand (operand0, SFmode))
    operands[1] = copy_to_mode_reg (SFmode, operand1);
}")

;; We could improve loading of some constants with a little work.
(define_insn ""
  [(set (match_operand:SF 0 "general_operand" "=d,a,d,dam,da")
	(match_operand:SF 1 "general_operand" "0,0,G,da,daim"))]
  "register_operand (operands[0], SFmode)
   || register_operand (operands[1], SFmode)"
  "@
  nop
  nop
  clr %0
  mov %1,%0
  mov %1,%0"
  [(set_attr "cc" "none,none,clobber,none_0hit,none_0hit")])


;; ----------------------------------------------------------------------
;; TEST INSTRUCTIONS
;; ----------------------------------------------------------------------

;; Go ahead and define tstsi so we can eliminate redundant tst insns
;; when we start trying to optimize this port.
(define_insn "tstsi"
  [(set (cc0) (match_operand:SI 0 "register_operand" "da"))]
  ""
  "cmp 0,%0"
  [(set_attr "cc" "set_zn_c0")])

(define_insn "cmpsi"
  [(set (cc0)
	(compare:SI (match_operand:SI 0 "register_operand" "da")
		    (match_operand:SI 1 "nonmemory_operand" "dai")))]
  ""
  "cmp %1,%0"
  [(set_attr "cc" "compare")])

;; ----------------------------------------------------------------------
;; ADD INSTRUCTIONS
;; ----------------------------------------------------------------------

(define_expand "addsi3"
  [(set (match_operand:SI 0 "register_operand" "=da,a,da,x")
	(plus:SI (match_operand:SI 1 "register_operand" "%0,0,0,0")
		 (match_operand:SI 2 "nonmemory_operand" "J,L,dai,i")))]
  ""
  "
{
  /* We can't add a variable amount directly to the stack pointer;
     so do so via a temporary register.  */
  if (operands[0] == stack_pointer_rtx
      && GET_CODE (operands[1]) != CONST_INT
      && GET_CODE (operands[2]) != CONST_INT)
   {
     rtx temp = gen_reg_rtx (SImode);
     emit_move_insn (temp, gen_rtx (PLUS, SImode, operands[1], operands[2]));
     emit_move_insn (operands[0], temp);
     DONE;
   }
}")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=d,a,a,da,x")
	(plus:SI (match_operand:SI 1 "register_operand" "%0,0,0,0,0")
		 (match_operand:SI 2 "nonmemory_operand" "J,J,L,dai,i")))]
  ""
  "@
  inc %0
  inc %0
  inc4 %0
  add %2,%0
  add %2,%0"
  [(set_attr "cc" "set,none_0hit,none_0hit,set,none_0hit")])

;; ----------------------------------------------------------------------
;; SUBTRACT INSTRUCTIONS
;; ----------------------------------------------------------------------

(define_insn "subsi3"
  [(set (match_operand:SI 0 "register_operand" "=da")
	(minus:SI (match_operand:SI 1 "register_operand" "0")
		  (match_operand:SI 2 "register_operand" "dai")))]
  ""
  "sub %2,%0"
  [(set_attr "cc" "set")])

(define_expand "negsi2"
  [(set (match_operand:SI 0 "register_operand" "")
        (neg:SI (match_operand:SI 1 "register_operand" "")))]
  ""
  "
{
  rtx target = gen_reg_rtx (SImode);

  emit_move_insn (target, GEN_INT (0));
  emit_insn (gen_subsi3 (target, target, operands[1]));
  emit_move_insn (operands[0], target);
  DONE;
}")


;; ----------------------------------------------------------------------
;; MULTIPLY INSTRUCTIONS
;; ----------------------------------------------------------------------

(define_insn "mulsi3"
  [(set (match_operand:SI 0 "register_operand" "=d")
	(mult:SI (match_operand:SI 1 "register_operand" "%0")
		 (match_operand:SI 2 "register_operand" "d")))]
  ""
  "mul %2,%0"
  [(set_attr "cc" "set_zn_c0")])

(define_insn "divsi3"
  [(set (match_operand:SI 0 "register_operand" "=d")
	(div:SI (match_operand:SI 1 "register_operand" "0")
		 (match_operand:SI 2 "register_operand" "d")))]
  ""
  "ext %0\;div %2,%0"
  [(set_attr "cc" "set_zn_c0")])

(define_expand "udivsi3"
  [(set (match_operand:SI 0 "register_operand" "")
	(udiv:SI (match_operand:SI 1 "register_operand" "")
		 (match_operand:SI 2 "register_operand" "")))]
  ""
  "
{
  rtx reg = gen_reg_rtx (SImode);
  emit_move_insn (reg, GEN_INT (0));
  emit_insn (gen_clear_mdr (reg));
}")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=d")
	(udiv:SI (match_operand:SI 1 "register_operand" "0")
		 (match_operand:SI 2 "register_operand" "d")))]
  ""
  "divu %2,%0"
  [(set_attr "cc" "set_zn_c0")])

(define_insn "clear_mdr"
  [(unspec_volatile [(const_int 2)] 0)
   (use (match_operand:SI 0 "register_operand" "d"))]
  ""
  "mov %0,mdr"
  [(set_attr "cc" "none")])

;; ----------------------------------------------------------------------
;; AND INSTRUCTIONS
;; ----------------------------------------------------------------------

(define_insn "andsi3"
  [(set (match_operand:SI 0 "register_operand" "=d")
	(and:SI (match_operand:SI 1 "register_operand" "%0")
		(match_operand:SI 2 "nonmemory_operand" "di")))]
  ""
  "and %2,%0"
  [(set_attr "cc" "set_zn_c0")])

;; ----------------------------------------------------------------------
;; OR INSTRUCTIONS
;; ----------------------------------------------------------------------

(define_insn "iorsi3"
  [(set (match_operand:SI 0 "register_operand" "=d")
	(ior:SI (match_operand:SI 1 "register_operand" "%0")
		(match_operand:SI 2 "nonmemory_operand" "di")))]
  ""
  "or %2,%0"
  [(set_attr "cc" "set_zn_c0")])

;; ----------------------------------------------------------------------
;; XOR INSTRUCTIONS
;; ----------------------------------------------------------------------

(define_insn "xorsi3"
  [(set (match_operand:SI 0 "register_operand" "=d")
	(xor:SI (match_operand:SI 1 "register_operand" "%0")
		(match_operand:SI 2 "nonmemory_operand" "di")))]
  ""
  "xor %2,%0"
  [(set_attr "cc" "set_zn_c0")])

;; ----------------------------------------------------------------------
;; NOT INSTRUCTIONS
;; ----------------------------------------------------------------------

(define_insn "one_cmplsi2"
  [(set (match_operand:SI 0 "register_operand" "=d")
	(not:SI (match_operand:SI 1 "register_operand" "0")))]
  ""
  "not %0"
  [(set_attr "cc" "set_zn_c0")])

;; -----------------------------------------------------------------
;; BIT FIELDS
;; -----------------------------------------------------------------
;; Is it worth defining insv and extv for the MN10300 series?!?
;; probably so.

;; -----------------------------------------------------------------
;; Scc INSTRUCTIONS
;; -----------------------------------------------------------------
;; It's probably worth the time to define setcc type insns too


;; ----------------------------------------------------------------------
;; JUMP INSTRUCTIONS
;; ----------------------------------------------------------------------

;; Conditional jump instructions

(define_expand "ble"
  [(set (pc)
	(if_then_else (le (cc0)
			  (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "")

(define_expand "bleu"
  [(set (pc)
	(if_then_else (leu (cc0)
			   (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "")

(define_expand "bge"
  [(set (pc)
	(if_then_else (ge (cc0)
			  (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "")

(define_expand "bgeu"
  [(set (pc)
	(if_then_else (geu (cc0)
			   (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "")

(define_expand "blt"
  [(set (pc)
	(if_then_else (lt (cc0)
			  (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "")

(define_expand "bltu"
  [(set (pc)
	(if_then_else (ltu (cc0)
			   (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "")

(define_expand "bgt"
  [(set (pc)
	(if_then_else (gt (cc0)
			  (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "")

(define_expand "bgtu"
  [(set (pc)
	(if_then_else (gtu (cc0)
			   (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "")

(define_expand "beq"
  [(set (pc)
	(if_then_else (eq (cc0)
			  (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "")

(define_expand "bne"
  [(set (pc)
	(if_then_else (ne (cc0)
			  (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "")

(define_insn ""
  [(set (pc)
	(if_then_else (match_operator 1 "comparison_operator"
				      [(cc0) (const_int 0)])
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "*
{
  if ((cc_status.flags & CC_OVERFLOW_UNUSABLE) != 0
      && (GET_CODE (operands[1]) == GT
	  || GET_CODE (operands[1]) == GE
	  || GET_CODE (operands[1]) == LE
	  || GET_CODE (operands[1]) == LT))
    return 0;
  return \"b%B1 0f\\n\\tjmp %0\\n0:\";
}"
 [(set_attr "cc" "none")])

(define_insn ""
  [(set (pc)
	(if_then_else (match_operator 1 "comparison_operator"
				      [(cc0) (const_int 0)])
		      (pc)
		      (label_ref (match_operand 0 "" ""))))]
  ""
  "*
{
  if ((cc_status.flags & CC_OVERFLOW_UNUSABLE) != 0
      && (GET_CODE (operands[1]) == GT
	  || GET_CODE (operands[1]) == GE
	  || GET_CODE (operands[1]) == LE
	  || GET_CODE (operands[1]) == LT))
    return 0;
  return \"b%b1 0f\\n\\tjmp %0\\n0:\";
}"
 [(set_attr "cc" "none")])

;; Unconditional and other jump instructions.

(define_insn "jump"
  [(set (pc)
	(label_ref (match_operand 0 "" "")))]
  ""
  "jmp %l0"
 [(set_attr "cc" "none")])

(define_insn "indirect_jump"
  [(set (pc) (match_operand:SI 0 "register_operand" "a"))]
  ""
  "jmp (%0)"
  [(set_attr "cc" "none")])

(define_insn "tablejump"
  [(set (pc) (match_operand:SI 0 "register_operand" "a"))
   (use (label_ref (match_operand 1 "" "")))]
  ""
  "jmp  (%0)"
  [(set_attr "cc" "none")])

;; Call subroutine with no return value.

(define_expand "call"
  [(call (match_operand:QI 0 "general_operand" "")
	 (match_operand:SI 1 "general_operand" ""))]
  ""
  "
{
  emit_insn (gen_addsi3 (stack_pointer_rtx,
			 stack_pointer_rtx,
			 GEN_INT (-4)));
  if (! call_address_operand (XEXP (operands[0], 0)))
    XEXP (operands[0], 0) = force_reg (SImode, XEXP (operands[0], 0));
  emit_call_insn (gen_call_internal (XEXP (operands[0], 0), operands[1]));
  emit_insn (gen_addsi3 (stack_pointer_rtx,
			 stack_pointer_rtx,
			 GEN_INT (4)));
  DONE;
}")

(define_insn "call_internal"
  [(call (mem:QI (match_operand:SI 0 "call_address_operand" "aS"))
	 (match_operand:SI 1 "general_operand" "g"))]
  ""
  "calls %C0"
  [(set_attr "cc" "clobber")])

;; Call subroutine, returning value in operand 0
;; (which must be a hard register).

(define_expand "call_value"
  [(set (match_operand 0 "" "")
	(call (match_operand:QI 1 "general_operand" "")
	      (match_operand:SI 2 "general_operand" "")))]
  ""
  "
{
  emit_insn (gen_addsi3 (stack_pointer_rtx,
			 stack_pointer_rtx,
			 GEN_INT (-4)));
  if (! call_address_operand (XEXP (operands[1], 0)))
    XEXP (operands[1], 0) = force_reg (SImode, XEXP (operands[1], 0));
  emit_call_insn (gen_call_value_internal (operands[0],
					   XEXP (operands[1], 0),
					   operands[2]));
  emit_insn (gen_addsi3 (stack_pointer_rtx,
			 stack_pointer_rtx,
			 GEN_INT (4)));
  DONE;
}")

(define_insn "call_value_internal"
  [(set (match_operand 0 "" "=d")
	(call (mem:QI (match_operand:SI 1 "call_address_operand" "aS"))
	      (match_operand:SI 2 "general_operand" "g")))]
  ""
  "calls %C1"
  [(set_attr "cc" "clobber")])

(define_insn "nop"
  [(const_int 0)]
  ""
  "nop"
  [(set_attr "cc" "none")])

;; ----------------------------------------------------------------------
;; EXTEND INSTRUCTIONS
;; ----------------------------------------------------------------------

(define_insn "zero_extendhisi2"
  [(set (match_operand:SI 0 "register_operand" "=d")
	(zero_extend:SI
	 (match_operand:HI 1 "register_operand" "0")))]
  ""
  "exthu %0"
  [(set_attr "cc" "none_0hit")])

(define_insn "zero_extendqisi2"
  [(set (match_operand:SI 0 "register_operand" "=d")
	(zero_extend:SI
	 (match_operand:QI 1 "register_operand" "0")))]
  ""
  "extbu %0"
  [(set_attr "cc" "none_0hit")])

;;- sign extension instructions

(define_insn "extendhisi2"
  [(set (match_operand:SI 0 "register_operand" "=d")
	(sign_extend:SI
	 (match_operand:HI 1 "register_operand" "0")))]
  ""
  "exth %0"
  [(set_attr "cc" "none_0hit")])

(define_insn "extendqisi2"
  [(set (match_operand:SI 0 "register_operand" "=d")
	(sign_extend:SI
	 (match_operand:QI 1 "register_operand" "0")))]
  ""
  "extb %0"
  [(set_attr "cc" "none_0hit")])


;; ----------------------------------------------------------------------
;; SHIFTS
;; ----------------------------------------------------------------------

(define_insn "ashlsi3"
  [(set (match_operand:SI 0 "register_operand" "=d,d")
	(ashift:SI
	 (match_operand:SI 1 "register_operand" "0,0")
	 (match_operand:QI 2 "nonmemory_operand" "K,di")))]
  ""
  "@
  asl2 %0
  asl %2,%0"
  [(set_attr "cc" "set_zn_c0")])

(define_insn "lshrsi3"
  [(set (match_operand:SI 0 "register_operand" "=d")
	(lshiftrt:SI
	 (match_operand:SI 1 "register_operand" "0")
	 (match_operand:QI 2 "nonmemory_operand" "di")))]
  ""
  "lsr %2,%0"
  [(set_attr "cc" "set_zn_c0")])

(define_insn "ashrsi3"
  [(set (match_operand:SI 0 "register_operand" "=d")
	(ashiftrt:SI
	 (match_operand:SI 1 "register_operand" "0")
	 (match_operand:QI 2 "nonmemory_operand" "di")))]
  ""
  "asr %2,%0"
  [(set_attr "cc" "set_zn_c0")])

;; ----------------------------------------------------------------------
;; PROLOGUE/EPILOGUE
;; ----------------------------------------------------------------------
(define_expand "prologue"
  [(const_int 0)]
  ""
  "expand_prologue (); DONE;")

(define_expand "epilogue"
  [(return)]
  ""
  "
{
  expand_epilogue ();
  DONE;
}")

(define_insn "return"
  [(return)]
  "0"
  "rets"
  [(set_attr "cc" "clobber")])

;; This insn restores the callee saved registers and does a return, it
;; can also deallocate stack space.
(define_insn "return_internal"
  [(const_int 0)
   (match_operand:SI 0  "const_int_operand" "i")
   (return)]
  ""
  "ret [d2,d3,a2,a3],%0"
  [(set_attr "cc" "clobber")])

(define_insn "store_movm"
  [(const_int 1)]
  ""
  "movm [d2,d3,a2,a3],(sp)"
  [(set_attr "cc" "clobber")])
