;; Machine description for MorphoRISC1
;; Copyright (C) 2005, 2007 Free Software Foundation, Inc.
;; Contributed by Red Hat, Inc.

;; This file is part of GCC.

;; GCC 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 3, or (at your option)
;; any later version.

;; GCC 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 GCC; see the file COPYING3.  If not see
;; <http://www.gnu.org/licenses/>.

;; UNSPECs
(define_constants
  [
    (UNSPEC_BLOCKAGE 0)
    (UNSPEC_EI 1)
    (UNSPEC_DI 2)
    (UNSPEC_LOOP 3)
  ])

;; Attributes
(define_attr "type" "branch,call,load,store,io,arith,complex,unknown"
	 (const_string "unknown") )

;; If the attribute takes numeric values, no `enum' type will be defined and
;; the function to obtain the attribute's value will return `int'.

(define_attr "length" "" (const_int 4))


;; DFA scheduler.
(define_automaton "other")
(define_cpu_unit "decode_unit" "other")
(define_cpu_unit "memory_unit" "other")
(define_cpu_unit "branch_unit" "other")

(define_insn_reservation "mem_access" 2
  (ior (eq_attr "type" "load") (eq_attr "type" "store"))
  "decode_unit+memory_unit*2")

(define_insn_reservation "io_access" 2
  (eq_attr "type" "io")
  "decode_unit+memory_unit*2")

(define_insn_reservation "branch_access" 2
  (ior (eq_attr "type" "branch")
       (eq_attr "type" "call"))
  "decode_unit+branch_unit*2")

(define_insn_reservation "arith_access" 1
  (eq_attr "type" "arith")
  "decode_unit")

(define_bypass 2 "arith_access" "branch_access")
(define_bypass 3 "mem_access" "branch_access")
(define_bypass 3 "io_access" "branch_access")


;; Delay Slots

;; The mt does not allow branches in the delay slot.
;; The mt does not allow back to back memory or io instruction.
;; The compiler does not know what the type of instruction is at
;; the destination of the branch.  Thus, only type that will be acceptable
;; (safe) is the arith type.

(define_delay (ior (eq_attr "type" "branch")
		   (eq_attr "type" "call"))
		 [(eq_attr "type" "arith") (nil) (nil)])


(define_insn "decrement_and_branch_until_zero"
   [(set (pc)
	 (if_then_else
	  (ne (match_operand:SI 0 "nonimmediate_operand" "+r,*m")
	      (const_int 0))
	  (label_ref (match_operand 1 "" ""))
	  (pc)))
    (set (match_dup 0)
	 (plus:SI (match_dup 0)
		  (const_int -1)))
    (clobber (match_scratch:SI 2 "=X,&r"))
    (clobber (match_scratch:SI 3 "=X,&r"))]
  "TARGET_MS1_16_003 || TARGET_MS2"
  "@
   dbnz\t%0, %l1%#
   #"
  [(set_attr "length" "4,16")
   (set_attr "type" "branch,unknown")]
)

;; Split the above to handle the case where operand 0 is in memory
;; (a register that couldn't get a hard register).
(define_split
  [(set (pc)
	(if_then_else
	  (ne (match_operand:SI 0 "memory_operand" "")
	      (const_int 0))
	  (label_ref (match_operand 1 "" ""))
	  (pc)))
    (set (match_dup 0)
	 (plus:SI (match_dup 0)
		  (const_int -1)))
    (clobber (match_scratch:SI 2 ""))
    (clobber (match_scratch:SI 3 ""))]
  "TARGET_MS1_16_003 || TARGET_MS2"
  [(set (match_dup 2) (match_dup 0))
   (set (match_dup 3) (plus:SI (match_dup 2) (const_int -1)))
   (set (match_dup 0) (match_dup 3))
   (set (pc)
	(if_then_else
	 (ne (match_dup 2)
	     (const_int 0))
	 (label_ref (match_dup 1))
	 (pc)))]
  "")

;; This peephole is defined in the vain hope that it might actually trigger one
;; day, although I have yet to find a test case that matches it.  The normal
;; problem is that GCC likes to move the loading of the constant value -1 out
;; of the loop, so it is not here to be matched.

(define_peephole2
  [(set (match_operand:SI 0 "register_operand" "")
	(plus:SI (match_dup 0) (const_int -1)))
   (set (match_operand:SI 1 "register_operand" "")
     (const_int -1))
   (set (pc) (if_then_else
	        (ne (match_dup 0) (match_dup 1))
		(label_ref (match_operand 2 "" ""))
		(pc)))]
  "TARGET_MS1_16_003 || TARGET_MS2"
  [(parallel [(set (pc)
	           (if_then_else
	              (ne (match_dup 0) (const_int 0))
	              (label_ref (match_dup 2))
	              (pc)))
              (set (match_dup 0)
	           (plus:SI (match_dup 0) (const_int -1)))
	      (clobber (reg:SI 0))
	      (clobber (reg:SI 0))])]
  "")


;; Loop instructions.  ms2 has a low overhead looping instructions.
;; these take a constant or register loop count and a loop length
;; offset.  Unfortunately the loop can only be up to 256 instructions,
;; We deal with longer loops by moving the loop end upwards.  To do
;; otherwise would force us to to be very pessimistic right up until
;; the end.

;; This instruction is a placeholder to make the control flow explicit.
(define_insn "loop_end"
  [(set (pc) (if_then_else
			  (ne (match_operand:SI 0 "register_operand" "")
			      (const_int 1))
			  (label_ref (match_operand 1 "" ""))
			  (pc)))
   (set (match_dup 0) (plus:SI (match_dup 0) (const_int -1)))
   (unspec [(const_int 0)] UNSPEC_LOOP)]
  "TARGET_MS2"
  ";loop end %0,%l1"
  [(set_attr "length" "0")])

;; This is the real looping instruction.  It is placed just before the
;; loop body.  We make it a branch insn, so it stays at the end of the
;; block it is in.
(define_insn "loop_init"
  [(set (match_operand:SI 0 "register_operand" "=r,r")
	(match_operand:SI 1 "uns_arith_operand" "r,K"))
   (unspec [(label_ref (match_operand 2 "" ""))] UNSPEC_LOOP)]
  "TARGET_MS2"
  "@
   loop  %1,%l2 ;%0%#
   loopi %1,%l2 ;%0%#"
  [(set_attr "length" "4")
   (set_attr "type" "branch")])

; operand 0 is the loop count pseudo register
; operand 1 is the number of loop iterations or 0 if it is unknown
; operand 2 is the maximum number of loop iterations
; operand 3 is the number of levels of enclosed loops
; operand 4 is the label to jump to at the top of the loop
(define_expand "doloop_end"
  [(parallel [(set (pc) (if_then_else
			  (ne (match_operand:SI 0 "nonimmediate_operand" "")
			      (const_int 0))
			  (label_ref (match_operand 4 "" ""))
			  (pc)))
	      (set (match_dup 0)
		   (plus:SI (match_dup 0)
			    (const_int -1)))
	      (clobber (match_scratch:SI 5 ""))
	      (clobber (match_scratch:SI 6 ""))])]
  "TARGET_MS1_16_003 || TARGET_MS2"
  {mt_add_loop ();})

;; Moves

(define_expand "loadqi"
  [
   ;; compute shift
   (set (match_operand:SI 2 "register_operand" "")
	(and:SI (match_dup 1) (const_int 3)))
   (set (match_dup 2)	(xor:SI (match_dup 2) (const_int 3)))
   (set (match_dup 2 )	(ashift:SI (match_dup 2) (const_int 3)))

   ;; get word that contains byte
   (set (match_operand:SI 0 "register_operand" "")
	(mem:SI (and:SI (match_operand:SI 1 "register_operand" "")
			(const_int -3))))

   ;; align byte
   (set (match_dup 0)   (ashiftrt:SI (match_dup 0) (match_dup 2)))
  ]
  ""
  "")


;; storeqi
;; operand 0 byte value to store
;; operand 1 address
;; operand 2 temp, word containing byte
;; operand 3 temp, shift count
;; operand 4 temp, mask, aligned and masked byte
;; operand 5 (unused)
(define_expand "storeqi"
  [
   ;; compute shift
   (set (match_operand:SI 3 "register_operand" "")
	(and:SI (match_operand:SI 1 "register_operand" "") (const_int 3)))
   (set (match_dup 3)	(xor:SI (match_dup 3) (const_int 3)))
   (set (match_dup 3)	(ashift:SI (match_dup 3) (const_int 3)))

   ;; get word that contains byte
   (set (match_operand:SI 2 "register_operand" "")
	(mem:SI (and:SI (match_dup 1) (const_int -3))))

   ;; generate mask
   (set (match_operand:SI 4 "register_operand" "") (const_int 255))
   (set (match_dup 4) (ashift:SI (match_dup 4) (match_dup 3)))
   (set (match_dup 4) (not:SI (match_dup 4)))

   ;; clear appropriate bits
   (set (match_dup 2) (and:SI (match_dup 2) (match_dup 4)))

   ;; align byte
   (set (match_dup 4)
	(and:SI (match_operand:SI 0 "register_operand" "") (const_int 255)))
   (set (match_dup 4) (ashift:SI (match_dup 4) (match_dup 3)))

   ;; combine
   (set (match_dup 2) (ior:SI (match_dup 4) (match_dup 2)))
   ;; store updated word
   (set (mem:SI (and:SI (match_dup 1) (const_int -3))) (match_dup 2))
  ]
  ""
  "")


(define_expand "movqi"
  [(set (match_operand:QI 0 "general_operand" "")
	(match_operand:QI 1 "general_operand" ""))]
  ""
  "
{
  if (!reload_in_progress
      && !reload_completed
      && GET_CODE (operands[0]) == MEM
      && GET_CODE (operands[1]) == MEM)
    operands[1] = copy_to_mode_reg (QImode, operands[1]);
  
  if ( (! TARGET_BYTE_ACCESS) && GET_CODE (operands[0]) == MEM)
    {
	rtx scratch1 = gen_reg_rtx (SImode);
	rtx scratch2 = gen_reg_rtx (SImode);
	rtx scratch3 = gen_reg_rtx (SImode);
	rtx data     = operands[1];
	rtx address  = XEXP (operands[0], 0);
	rtx seq;

	if ( GET_CODE (data) != REG )
	    data = copy_to_mode_reg (QImode, data);

	if ( GET_CODE (address) != REG )
	  address = copy_to_mode_reg (SImode, address);

	start_sequence ();
	emit_insn (gen_storeqi (gen_lowpart (SImode, data), address,
				scratch1, scratch2, scratch3));
	mt_set_memflags (operands[0]);
	seq = get_insns ();
	end_sequence ();
	emit_insn (seq);
	DONE;
    }

  if ( (! TARGET_BYTE_ACCESS) && GET_CODE (operands[1]) == MEM)
    {
	rtx scratch1 = gen_reg_rtx (SImode);
	rtx data = operands[0];
	rtx address = XEXP (operands[1], 0);
	rtx seq;

	if ( GET_CODE (address) != REG )
	  address = copy_to_mode_reg (SImode, address);

	start_sequence ();
	emit_insn (gen_loadqi (gen_lowpart (SImode, data), address, scratch1));
	mt_set_memflags (operands[1]);
	seq = get_insns ();
	end_sequence ();
	emit_insn (seq);
	DONE;
    }

   /* If the load is a pseudo register in a stack slot, some simplification
      can be made because the loads are aligned */
  if ( (! TARGET_BYTE_ACCESS) 
        && (reload_in_progress && GET_CODE (operands[1]) == SUBREG
	  && GET_CODE (SUBREG_REG (operands[1])) == REG
	  && REGNO (SUBREG_REG (operands[1])) >= FIRST_PSEUDO_REGISTER))
    {
	rtx data = operands[0];
	rtx address = XEXP (operands[1], 0);
	rtx seq;

	start_sequence ();
	emit_insn (gen_movsi (gen_lowpart (SImode, data), address));
	mt_set_memflags (operands[1]);
	seq = get_insns ();
	end_sequence ();
	emit_insn (seq);
	DONE;
    }
}")

(define_insn "*movqi_internal"
  [(set (match_operand:QI 0 "nonimmediate_operand" "=r,r,m,r")
	(match_operand:QI 1 "general_operand" "r,m,r,I"))]
  "TARGET_BYTE_ACCESS
    && (!memory_operand (operands[0], QImode)
        || !memory_operand (operands[1], QImode))"
  "@
   or  %0, %1, %1
   ldb %0, %1
   stb %1, %0
   addi %0, r0, %1"
  [(set_attr "length" "4,4,4,4")
   (set_attr "type" "arith,load,store,arith")])

(define_insn "*movqi_internal_nobyte"
  [(set (match_operand:QI 0 "register_operand" "=r,r")
	(match_operand:QI 1 "arith_operand" "r,I"))]
  "!TARGET_BYTE_ACCESS
    && (!memory_operand (operands[0], QImode)
        || !memory_operand (operands[1], QImode))"
  "@
   or   %0, %1, %1
   addi %0, r0, %1"
  [(set_attr "length" "4,4")
   (set_attr "type" "arith,arith")])


;; The MorphoRISC does not have 16-bit loads and stores.
;; These operations must be synthesized.  Note that the code
;; for loadhi and storehi assumes that the least significant bits
;; is ignored.

;; loadhi
;; operand 0 location of result
;; operand 1 memory address
;; operand 2 temp
(define_expand "loadhi"
  [
   ;; compute shift
   (set (match_operand:SI 2 "register_operand" "")
	(and:SI (match_dup 1) (const_int 2)))
   (set (match_dup 2)	(xor:SI (match_dup 2) (const_int 2)))
   (set (match_dup 2 )	(ashift:SI (match_dup 2) (const_int 3)))

   ;; get word that contains the 16-bits
   (set (match_operand:SI 0 "register_operand" "")
	(mem:SI (and:SI (match_operand:SI 1 "register_operand" "")
			(const_int -3))))

   ;; align 16-bit value
   (set (match_dup 0)	(ashiftrt:SI (match_dup 0) (match_dup 2)))
  ]
  ""
  "")

;; storehi
;; operand 0 byte value to store
;; operand 1 address
;; operand 2 temp, word containing byte
;; operand 3 temp, shift count
;; operand 4 temp, mask, aligned and masked byte
;; operand 5 (unused)
(define_expand "storehi"
  [
   ;; compute shift
   (set (match_operand:SI 3 "register_operand" "")
	(and:SI (match_operand:SI 1 "register_operand" "") (const_int 2)))
   (set (match_dup 3)	(xor:SI (match_dup 3) (const_int 2)))
   (set (match_dup 3)	(ashift:SI (match_dup 3) (const_int 3)))

   ;; get word that contains the 16-bits
   (set (match_operand:SI 2 "register_operand" "")
	(mem:SI (and:SI (match_dup 1) (const_int -3))))

   ;; generate mask
   (set (match_operand:SI 4 "register_operand" "") (const_int 65535))
   (set (match_dup 4) (ashift:SI (match_dup 4) (match_dup 3)))
   (set (match_dup 4) (not:SI (match_dup 4)))

   ;; clear appropriate bits
   (set (match_dup 2) (and:SI (match_dup 2) (match_dup 4)))

   ;; align 16-bit value
   (set (match_dup 4)
	(and:SI (match_operand:SI 0 "register_operand" "") (const_int 65535)))
   (set (match_dup 4) (ashift:SI (match_dup 4) (match_dup 3)))

   ;; combine
   (set (match_dup 2) (ior:SI (match_dup 4) (match_dup 2)))
   ;; store updated word
   (set (mem:SI (and:SI (match_dup 1) (const_int -3))) (match_dup 2))
  ]
  ""
  "")


(define_expand "movhi"
  [(set (match_operand:HI 0 "general_operand" "")
	(match_operand:HI 1 "general_operand" ""))]
  ""
  "
{
  if (!reload_in_progress
      && !reload_completed
      && GET_CODE (operands[0]) == MEM
      && GET_CODE (operands[1]) == MEM)
    operands[1] = copy_to_mode_reg (HImode, operands[1]);

  if ( GET_CODE (operands[0]) == MEM)
    {
	rtx scratch1 = gen_reg_rtx (SImode);
	rtx scratch2 = gen_reg_rtx (SImode);
	rtx scratch3 = gen_reg_rtx (SImode);
	rtx data     = operands[1];
	rtx address  = XEXP (operands[0], 0);
	rtx seq;

	if (GET_CODE (data) != REG)
	  data = copy_to_mode_reg (HImode, data);

	if (GET_CODE (address) != REG)
	  address = copy_to_mode_reg (SImode, address);

	start_sequence ();
	emit_insn (gen_storehi (gen_lowpart (SImode, data), address,
			        scratch1, scratch2, scratch3));
	mt_set_memflags (operands[0]);
	seq = get_insns ();
	end_sequence ();
	emit_insn (seq);
	DONE;
    }

  if ( GET_CODE (operands[1]) == MEM)
    {
	rtx scratch1 = gen_reg_rtx (SImode);
	rtx data     = operands[0];
	rtx address  = XEXP (operands[1], 0);
	rtx seq;

	if (GET_CODE (address) != REG)
	    address = copy_to_mode_reg (SImode, address);

	start_sequence ();
	emit_insn (gen_loadhi (gen_lowpart (SImode, data), address,
			       scratch1));
	mt_set_memflags (operands[1]);
	seq = get_insns ();
	end_sequence ();
	emit_insn (seq);
	DONE;
    }

   /* If the load is a pseudo register in a stack slot, some simplification
      can be made because the loads are aligned */
  if ( (reload_in_progress && GET_CODE (operands[1]) == SUBREG
	  && GET_CODE (SUBREG_REG (operands[1])) == REG
	  && REGNO (SUBREG_REG (operands[1])) >= FIRST_PSEUDO_REGISTER))
    {
	rtx data = operands[0];
	rtx address = XEXP (operands[1], 0);
	rtx seq;

	start_sequence ();
	emit_insn (gen_movsi (gen_lowpart (SImode, data), address));
	mt_set_memflags (operands[1]);
	seq = get_insns ();
	end_sequence ();
	emit_insn (seq);
	DONE;
    }
}")

(define_insn "*movhi_internal"
  [(set (match_operand:HI 0 "register_operand" "=r,r")
	(match_operand:HI 1 "arith_operand" "r,I"))]
  "!memory_operand (operands[0], HImode) || !memory_operand (operands[1], HImode)"
  "@
  or    %0, %1, %1
  addi  %0, r0, %1"
  [(set_attr "length" "4,4")
   (set_attr "type" "arith,arith")])

(define_expand "movsi"
  [(set (match_operand:SI 0 "nonimmediate_operand" "")
	(match_operand:SI 1 "general_operand" ""))]
  ""
  "
{
  if (!reload_in_progress  && !reload_completed
      && !register_operand (operands[0], SImode)
      && !register_operand (operands[1], SImode))
    operands[1] = copy_to_mode_reg (SImode, operands[1]);

  /* Take care of constants that don't fit in single instruction */
  if ( (reload_in_progress || reload_completed)
   && !single_const_operand (operands[1], SImode))
    {
      emit_insn (gen_movsi_high (operands[0], operands[1]));
      emit_insn (gen_movsi_lo_sum (operands[0], operands[0], operands[1]));
      DONE;
    }

}")

(define_insn "movsi_high"
  [(set (match_operand:SI 0 "register_operand" "=r")
        (high:SI (match_operand:SI 1 "general_operand" "i")))]
  ""
  "*
{
  return \"ldui\\t%0, %H1\";
}"
  [(set_attr "length" "4")
   (set_attr "type" "arith")])


(define_insn "movsi_lo_sum"
  [(set (match_operand:SI 0 "register_operand" "=r")
        (lo_sum:SI (match_operand:SI 1 "register_operand" "r")
                   (match_operand:SI 2 "general_operand" "i")))]
  ""
  "*
{
  return \"addui\\t%0, %1, %L2\";
}"
  [(set_attr "length" "4")
   (set_attr "type" "arith")])

/* Take care of constants that don't fit in single instruction */
(define_split
  [(set (match_operand:SI 0 "register_operand" "")
	(match_operand:SI 1 "general_operand" ""))]
  "(reload_in_progress || reload_completed)
   && !single_const_operand (operands[1], SImode)"

  [(set (match_dup 0 )
        (high:SI (match_dup 1)))
   (set (match_dup 0 )
        (lo_sum:SI (match_dup 0)
                   (match_dup 1)))]
)


;; The last pattern in movsi (with two instructions)
;; is really handled by the emit_insn's in movsi
;; and the define_split above.  This provides additional
;; instructions to fill delay slots.

;; Note - it is best to only have one movsi pattern and to handle
;; all the various contingencies by the use of alternatives.  This
;; allows reload the greatest amount of flexibility (since reload will
;; only choose amoungst alternatives for a selected insn, it will not
;; replace the insn with another one).
(define_insn "*movsi_internal"
  [(set (match_operand:SI 0 "nonimmediate_operand" "=r,r,m,r,r,r,r,r")
	(match_operand:SI 1 "general_operand"       "r,m,r,I,P,L,N,i"))]
  "(!memory_operand (operands[0], SImode) || !memory_operand (operands[1], SImode))
   && !((reload_in_progress || reload_completed)
	 && !single_const_operand (operands[1], SImode))"
  "@
  or     %0, %1, %1
  ldw    %0, %1
  stw    %1, %0
  addi   %0, r0, %1
  addui  %0, r0, %1
  ldui   %0, %H1
  nori   %0, r0, %N1
  ldui   %0, %H1\;addui %0, %0, %L1"
  [(set_attr "length" "4,4,4,4,4,4,4,8")
   (set_attr "type" "arith,load,store,arith,arith,arith,arith,complex")]
)

;; Floating Point Moves
;;
;; Note - Patterns for SF mode moves are compulsory, but
;; patterns for DF are optional, as GCC can synthesize them.

(define_expand "movsf"
  [(set (match_operand:SF 0 "general_operand" "")
	(match_operand:SF 1 "general_operand" ""))]
  ""
  "
{
  if (!reload_in_progress
      && !reload_completed
      && GET_CODE (operands[0]) == MEM
      && (GET_CODE (operands[1]) == MEM
         || GET_CODE (operands[1]) == CONST_DOUBLE))
    operands[1] = copy_to_mode_reg (SFmode, operands[1]);

  /* Take care of reg <- SF constant */
  if ( const_double_operand (operands[1], GET_MODE (operands[1]) ) )
    {
      emit_insn (gen_movsf_high (operands[0], operands[1]));
      emit_insn (gen_movsf_lo_sum (operands[0], operands[0], operands[1]));
      DONE;
    }
}")

(define_insn "movsf_lo_sum"
  [(set (match_operand:SF 0 "register_operand" "=r")
        (lo_sum:SF (match_operand:SF 1 "register_operand" "r")
                   (match_operand:SF 2 "const_double_operand" "")))]
  ""
  "*
{
  REAL_VALUE_TYPE r;
  long i;

  REAL_VALUE_FROM_CONST_DOUBLE (r, operands[2]);
  REAL_VALUE_TO_TARGET_SINGLE (r, i);
  operands[2] = GEN_INT (i);
  return \"addui\\t%0, %1, %L2\";
}"
  [(set_attr "length" "4")
   (set_attr "type" "arith")])

(define_insn "movsf_high"
  [(set (match_operand:SF 0 "register_operand" "=r")
        (high:SF (match_operand:SF 1 "const_double_operand" "")))]
  ""
  "*
{
  REAL_VALUE_TYPE r;
  long i;

  REAL_VALUE_FROM_CONST_DOUBLE (r, operands[1]);
  REAL_VALUE_TO_TARGET_SINGLE (r, i);
  operands[1] = GEN_INT (i);
  return \"ldui\\t%0, %H1\";
}"
  [(set_attr "length" "4")
   (set_attr "type" "arith")])


(define_insn "*movsf_internal"
  [(set (match_operand:SF 0 "nonimmediate_operand" "=r,r,m")
	(match_operand:SF 1 "nonimmediate_operand" "r,m,r"))]
  "!memory_operand (operands[0], SFmode) || !memory_operand (operands[1], SFmode)"
  "@
  or     %0, %1, %1
  ldw    %0, %1
  stw    %1, %0"
  [(set_attr "length" "4,4,4")
   (set_attr "type" "arith,load,store")]
)

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

(define_insn_and_split "*movdf_internal"
  [(set (match_operand:DF 0 "nonimmediate_operand" "=r,o")
	(match_operand:DF 1 "general_operand" 	   "rim,r"))]
  "! (memory_operand (operands[0], DFmode)
         && memory_operand (operands[1], DFmode))"
  "#"

  "(reload_completed || reload_in_progress)"

  [(set (match_dup 2) (match_dup 3))
   (set (match_dup 4) (match_dup 5))
  ]

  "{
    /* figure out what precisely to put into operands 2, 3, 4, and 5 */
    mt_split_words (SImode, DFmode, operands);
  }"
)


;; Reloads

;; Like `movM', but used when a scratch register is required to move between
;; operand 0 and operand 1.  Operand 2 describes the scratch register.  See the
;; discussion of the `SECONDARY_RELOAD_CLASS' macro.

(define_expand "reload_inqi"
  [(set (match_operand:QI 0 "register_operand" "=r")
        (match_operand:QI 1 "memory_operand" "m"))
   (clobber (match_operand:DI 2 "register_operand" "=&r"))]
  "! TARGET_BYTE_ACCESS"
  "
{
  rtx scratch1 = gen_rtx_REG (SImode, REGNO (operands[2]));
  rtx scratch2 = gen_rtx_REG (SImode, REGNO (operands[2])+1);
  rtx data = operands[0];
  rtx address = XEXP (operands[1], 0);
  rtx swap, seq;

  /* It is possible that the registers we got for scratch1
     might coincide with that of operands[0].  gen_loadqi
     requires operand0 and operand2 to be different registers.
     The following statement ensure that is always the case. */
  if (REGNO(operands[0]) == REGNO(scratch1))
    {
	swap = scratch1;
	scratch1 = scratch2;
	scratch2 = swap;
    }

  /* need to make sure address is already in register */
  if ( GET_CODE (address) != REG )
    address = force_operand (address, scratch2);

  start_sequence ();
  emit_insn (gen_loadqi (gen_lowpart (SImode, data), address, scratch1));
  mt_set_memflags (operands[1]);
  seq = get_insns ();
  end_sequence ();
  emit_insn (seq);
  DONE;
}")

(define_expand "reload_outqi"
  [(set (match_operand:QI 0 "memory_operand" "=m")
        (match_operand:QI 1 "register_operand" "r"))
   (clobber (match_operand:TI 2 "register_operand" "=&r"))]
  "! TARGET_BYTE_ACCESS"
  "
{
  rtx scratch1 = gen_rtx_REG (SImode, REGNO (operands[2]));
  rtx scratch2 = gen_rtx_REG (SImode, REGNO (operands[2])+1);
  rtx scratch3 = gen_rtx_REG (SImode, REGNO (operands[2])+2);
  rtx scratch4 = gen_rtx_REG (SImode, REGNO (operands[2])+3);
  rtx data     = operands[1];
  rtx address  = XEXP (operands[0], 0);
  rtx seq;

  /* need to make sure address is already in register */
  if ( GET_CODE (address) != REG )
    address = force_operand (address, scratch4);

  start_sequence ();
  emit_insn (gen_storeqi (gen_lowpart (SImode, data), address, 
			  scratch1, scratch2, scratch3));
  mt_set_memflags (operands[0]);
  seq = get_insns ();
  end_sequence ();
  emit_insn (seq);
  DONE;
}")

(define_expand "reload_inhi"
  [(set (match_operand:HI 0 "register_operand" "=r")
        (match_operand:HI 1 "memory_operand" "m"))
   (clobber (match_operand:DI 2 "register_operand" "=&r"))]
  ""
  "
{
  rtx scratch1 = gen_rtx_REG (SImode, REGNO (operands[2]));
  rtx scratch2 = gen_rtx_REG (SImode, REGNO (operands[2])+1);
  rtx data     = operands[0];
  rtx address  = XEXP (operands[1], 0);
  rtx swap, seq;

  /* It is possible that the registers we got for scratch1
     might coincide with that of operands[0].  gen_loadqi
     requires operand0 and operand2 to be different registers.
     The following statement ensure that is always the case. */
  if (REGNO(operands[0]) == REGNO(scratch1))
    {
	swap = scratch1;
	scratch1 = scratch2;
	scratch2 = swap;
    }

  /* need to make sure address is already in register */
  if ( GET_CODE (address) != REG )
    address = force_operand (address, scratch2);

  start_sequence ();
  emit_insn (gen_loadhi (gen_lowpart (SImode, data), address,
		         scratch1));
  mt_set_memflags (operands[1]);
  seq = get_insns ();
  end_sequence ();
  emit_insn (seq);
  DONE;
}")

(define_expand "reload_outhi"
  [(set (match_operand:HI 0 "memory_operand" "=m")
        (match_operand:HI 1 "register_operand" "r"))
   (clobber (match_operand:TI 2 "register_operand" "=&r"))]
  ""
  "
{
  rtx scratch1 = gen_rtx_REG (SImode, REGNO (operands[2]));
  rtx scratch2 = gen_rtx_REG (SImode, REGNO (operands[2])+1);
  rtx scratch3 = gen_rtx_REG (SImode, REGNO (operands[2])+2);
  rtx scratch4 = gen_rtx_REG (SImode, REGNO (operands[2])+3);
  rtx data     = operands[1];
  rtx address  = XEXP (operands[0], 0);
  rtx seq;

  /* need to make sure address is already in register */
  if ( GET_CODE (address) != REG )
    address = force_operand (address, scratch4);

  start_sequence ();
  emit_insn (gen_storehi (gen_lowpart (SImode, data), address,
		          scratch1, scratch2, scratch3));
  mt_set_memflags (operands[0]);
  seq = get_insns ();
  end_sequence ();
  emit_insn (seq);
  DONE;
}")


;; 32-bit Integer arithmetic

;; Addition
(define_insn "addsi3"
  [(set (match_operand:SI 0 "register_operand" "=r,r")
	(plus:SI (match_operand:SI 1 "register_operand" "%r,r")
		 (match_operand:SI 2 "arith_operand" "r,I")))]
  ""
  "@
  add %0, %1, %2
  addi %0, %1, %2"
  [(set_attr "length" "4,4")
   (set_attr "type" "arith,arith")])

;; Subtraction
(define_insn "subsi3"
  [(set (match_operand:SI 0 "register_operand" "=r,r")
	(minus:SI (match_operand:SI 1 "reg_or_0_operand" "rJ,rJ")
		  (match_operand:SI 2 "arith_operand" "rJ,I")))]
  ""
  "@
  sub %0, %z1, %z2
  subi %0, %z1, %2"
  [(set_attr "length" "4,4")
   (set_attr "type" "arith,arith")])

;;  Negation 
(define_insn "negsi2"
  [(set (match_operand:SI 0 "register_operand" "=r,r")
	(neg:SI (match_operand:SI 1 "arith_operand" "r,I")))]
  ""
  "@
  sub  %0, r0, %1
  subi  %0, r0, %1"
  [(set_attr "length" "4,4")
   (set_attr "type" "arith,arith")])


;; 32-bit Integer Shifts and Rotates

;; Arithmetic Shift Left
(define_insn "ashlsi3"
  [(set (match_operand:SI 0 "register_operand" "=r,r")
	(ashift:SI (match_operand:SI 1 "register_operand" "r,r")
		   (match_operand:SI 2 "arith_operand" "r,K")))]
  ""
  "@
  lsl %0, %1, %2
  lsli %0, %1, %2"
  [(set_attr "length" "4,4")
   (set_attr "type" "arith,arith")])

;; Arithmetic Shift Right
(define_insn "ashrsi3"
  [(set (match_operand:SI 0 "register_operand" "=r,r")
	(ashiftrt:SI (match_operand:SI 1 "register_operand" "r,r")
		     (match_operand:SI 2 "uns_arith_operand" "r,K")))]
  ""
  "@
  asr %0, %1, %2
  asri %0, %1, %2"
  [(set_attr "length" "4,4")
   (set_attr "type" "arith,arith")])

;; Logical Shift Right
(define_insn "lshrsi3"
  [(set (match_operand:SI 0 "register_operand" "=r,r")
	(lshiftrt:SI (match_operand:SI 1 "register_operand" "r,r")
		     (match_operand:SI 2 "uns_arith_operand" "r,K")))]
  ""
  "@
  lsr %0, %1, %2
  lsri %0, %1, %2"
  [(set_attr "length" "4,4")
   (set_attr "type" "arith,arith")])


;; 32-Bit Integer Logical operations

;; Logical AND, 32-bit integers
(define_insn "andsi3"
  [(set (match_operand:SI 0 "register_operand" "=r,r")
	(and:SI (match_operand:SI 1 "register_operand" "%r,r")
		(match_operand:SI 2 "uns_arith_operand" "r,K")))]
  ""
  "@
  and %0, %1, %2
  andi %0, %1, %2"
  [(set_attr "length" "4,4")
   (set_attr "type" "arith,arith")])

;; Inclusive OR, 32-bit integers
(define_insn "iorsi3"
  [(set (match_operand:SI 0 "register_operand" "=r,r")
	(ior:SI (match_operand:SI 1 "register_operand" "%r,r")
		(match_operand:SI 2 "uns_arith_operand" "r,K")))]
  ""
  "@
  or %0, %1, %2
  ori %0, %1, %2"
  [(set_attr "length" "4,4")
   (set_attr "type" "arith,arith")])

;; Exclusive OR, 32-bit integers
(define_insn "xorsi3"
  [(set (match_operand:SI 0 "register_operand" "=r,r")
	(xor:SI (match_operand:SI 1 "register_operand" "%r,r")
		(match_operand:SI 2 "uns_arith_operand" "r,K")))]
  ""
  "@
  xor %0, %1, %2
  xori %0, %1, %2"
  [(set_attr "length" "4,4")
   (set_attr "type" "arith,arith")])


;; One's complement, 32-bit integers
(define_insn "one_cmplsi2"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(not:SI (match_operand:SI 1 "register_operand" "r")))]
  ""
  "nor %0, %1, %1"
  [(set_attr "length" "4")
   (set_attr "type" "arith")])


;; Multiply

(define_insn "mulhisi3"
  [(set (match_operand:SI 0 "register_operand" "=r,r")
     (mult:SI (sign_extend:SI (match_operand:HI 1 "register_operand" "%r,r"))
     	      (sign_extend:SI (match_operand:HI 2 "arith_operand" "r,I"))))]
  "TARGET_MS1_16_003 || TARGET_MS2"
  "@
  mul %0, %1, %2
  muli %0, %1, %2"
  [(set_attr "length" "4,4")
   (set_attr "type" "arith,arith")])


;; Comparisons

;; Note, we store the operands in the comparison insns, and use them later
;; when generating the branch or scc operation.

;; First the routines called by the machine independent part of the compiler
(define_expand "cmpsi"
  [(set (cc0)
        (compare (match_operand:SI 0 "register_operand" "")
  		 (match_operand:SI 1 "arith_operand" "")))]
  ""
  "
{
  mt_compare_op0 = operands[0];
  mt_compare_op1 = operands[1];
  DONE;
}")


;; Branches

(define_expand "beq"
  [(use (match_operand 0 "" ""))]
  ""
  "
{
  mt_emit_cbranch (EQ, operands[0], mt_compare_op0, mt_compare_op1);
  DONE;
}")

(define_expand "bne"
  [(use (match_operand 0 "" ""))]
  ""
  "
{
  mt_emit_cbranch (NE, operands[0], mt_compare_op0, mt_compare_op1);
  DONE;
}")

(define_expand "bge"
  [(use (match_operand 0 "" ""))]
  ""
  "
{
  mt_emit_cbranch (GE, operands[0], mt_compare_op0, mt_compare_op1);
  DONE;
}")

(define_expand "bgt"
  [(use (match_operand 0 "" ""))]
  ""
  "
{
  mt_emit_cbranch (GT, operands[0], mt_compare_op0, mt_compare_op1);
  DONE;
}")

(define_expand "ble"
  [(use (match_operand 0 "" ""))]
  ""
  "
{
  mt_emit_cbranch (LE, operands[0], mt_compare_op0, mt_compare_op1);
  DONE;
}")

(define_expand "blt"
  [(use (match_operand 0 "" ""))]
  ""
  "
{
  mt_emit_cbranch (LT, operands[0], mt_compare_op0, mt_compare_op1);
  DONE;
}")

(define_expand "bgeu"
  [(use (match_operand 0 "" ""))]
  ""
  "
{
  mt_emit_cbranch (GEU, operands[0], mt_compare_op0, mt_compare_op1);
  DONE;
}")

(define_expand "bgtu"
  [(use (match_operand 0 "" ""))]
  ""
  "
{
  mt_emit_cbranch (GTU, operands[0], mt_compare_op0, mt_compare_op1);
  DONE;
}")

(define_expand "bleu"
  [(use (match_operand 0 "" ""))]
  ""
  "
{
  mt_emit_cbranch (LEU, operands[0], mt_compare_op0, mt_compare_op1);
  DONE;
}")

(define_expand "bltu"
  [(use (match_operand 0 "" ""))]
  ""
  "
{
  mt_emit_cbranch (LTU, operands[0], mt_compare_op0, mt_compare_op1);
  DONE;
}")

(define_expand "bunge"
  [(use (match_operand 0 "" ""))]
  ""
  "
{
  mt_emit_cbranch (GEU, operands[0], mt_compare_op0, mt_compare_op1);
  DONE;
}")

(define_expand "bungt"
  [(use (match_operand 0 "" ""))]
  ""
  "
{
  mt_emit_cbranch (GTU, operands[0], mt_compare_op0, mt_compare_op1);
  DONE;
}")

(define_expand "bunle"
  [(use (match_operand 0 "" ""))]
  ""
  "
{
  mt_emit_cbranch (LEU, operands[0], mt_compare_op0, mt_compare_op1);
  DONE;
}")

(define_expand "bunlt"
  [(use (match_operand 0 "" ""))]
  ""
  "
{
  mt_emit_cbranch (LTU, operands[0], mt_compare_op0, mt_compare_op1);
  DONE;
}")

(define_insn "*beq_true"
  [(set (pc)
	(if_then_else (eq (match_operand:SI 0 "reg_or_0_operand" "rJ")
			  (match_operand:SI 1 "reg_or_0_operand" "rJ"))
		      (label_ref (match_operand 2 "" ""))
		      (pc)))]
  ""
  "breq %z0, %z1, %l2%#"
  [(set_attr "length" "4")
   (set_attr "type" "branch")])

(define_insn "*beq_false"
  [(set (pc)
	(if_then_else (eq (match_operand:SI 0 "reg_or_0_operand" "rJ")
			  (match_operand:SI 1 "reg_or_0_operand" "rJ"))
		      (pc)
		      (label_ref (match_operand 2 "" ""))))]
  ""
  "brne %z0, %z1, %l2%#"
  [(set_attr "length" "4")
   (set_attr "type" "branch")])


(define_insn "*bne_true"
  [(set (pc)
	(if_then_else (ne (match_operand:SI 0 "reg_or_0_operand" "rJ")
			  (match_operand:SI 1 "reg_or_0_operand" "rJ"))
		      (label_ref (match_operand 2 "" ""))
		      (pc)))]
  ""
  "brne %z0, %z1, %l2%#"
  [(set_attr "length" "4")
   (set_attr "type" "branch")])

(define_insn "*bne_false"
  [(set (pc)
	(if_then_else (ne (match_operand:SI 0 "reg_or_0_operand" "rJ")
			  (match_operand:SI 1 "reg_or_0_operand" "rJ"))
		      (pc)
		      (label_ref (match_operand 2 "" ""))))]
  ""
  "breq %z0, %z1, %l2%#"
  [(set_attr "length" "4")
   (set_attr "type" "branch")])

(define_insn "*blt_true"
  [(set (pc)
	(if_then_else (lt (match_operand:SI 0 "reg_or_0_operand" "rJ")
			  (match_operand:SI 1 "reg_or_0_operand" "rJ"))
		      (label_ref (match_operand 2 "" ""))
		      (pc)))]
  ""
  "brlt %z0, %z1, %l2%#"
  [(set_attr "length" "4")
   (set_attr "type" "branch")])

(define_insn "*blt_false"
  [(set (pc)
	(if_then_else (lt (match_operand:SI 0 "reg_or_0_operand" "rJ")
			  (match_operand:SI 1 "reg_or_0_operand" "rJ"))
		      (pc)
		      (label_ref (match_operand 2 "" ""))))]
  ""
  "brle %z1, %z0,%l2%#"
  [(set_attr "length" "4")
   (set_attr "type" "branch")])

(define_insn "*ble_true"
  [(set (pc)
	(if_then_else (le (match_operand:SI 0 "reg_or_0_operand" "rJ")
			  (match_operand:SI 1 "reg_or_0_operand" "rJ"))
		      (label_ref (match_operand 2 "" ""))
		      (pc)))]
  ""
  "brle %z0, %z1, %l2%#"
  [(set_attr "length" "4")
   (set_attr "type" "branch")])

(define_insn "*ble_false"
  [(set (pc)
	(if_then_else (le (match_operand:SI 0 "reg_or_0_operand" "rJ")
			  (match_operand:SI 1 "reg_or_0_operand" "rJ"))
		      (pc)
		      (label_ref (match_operand 2 "" ""))))]
  ""
  "brlt %z1, %z0,%l2%#"
  [(set_attr "length" "4")
   (set_attr "type" "branch")])

(define_insn "*bgt_true"
  [(set (pc)
	(if_then_else (gt (match_operand:SI 0 "reg_or_0_operand" "rJ")
			  (match_operand:SI 1 "reg_or_0_operand" "rJ"))
		      (label_ref (match_operand 2 "" ""))
		      (pc)))]
  ""
  "brlt %z1, %z0, %l2%#"
  [(set_attr "length" "4")
   (set_attr "type" "branch")])

(define_insn "*bgt_false"
  [(set (pc)
	(if_then_else (gt (match_operand:SI 0 "reg_or_0_operand" "rJ")
			  (match_operand:SI 1 "reg_or_0_operand" "rJ"))
		      (pc)
		      (label_ref (match_operand 2 "" ""))))]
  ""
  "brle %z0, %z1, %l2%#"
  [(set_attr "length" "4")
   (set_attr "type" "branch")])

(define_insn "*bge_true"
  [(set (pc)
	(if_then_else (ge (match_operand:SI 0 "reg_or_0_operand" "rJ")
			  (match_operand:SI 1 "reg_or_0_operand" "rJ"))
		      (label_ref (match_operand 2 "" ""))
		      (pc)))]
  ""
  "brle %z1, %z0,%l2%#"
  [(set_attr "length" "4")
   (set_attr "type" "branch")])

(define_insn "*bge_false"
  [(set (pc)
	(if_then_else (ge (match_operand:SI 0 "reg_or_0_operand" "rJ")
			  (match_operand:SI 1 "reg_or_0_operand" "rJ"))
		      (pc)
		      (label_ref (match_operand 2 "" ""))))]
  ""
  "brlt %z0, %z1, %l2%#"
  [(set_attr "length" "4")
   (set_attr "type" "branch")])

;; No unsigned operators on Morpho mt.  All the unsigned operations are
;; converted to the signed operations above.


;; Set flag operations

;; "seq", "sne", "slt", "sle", "sgt", "sge", "sltu", "sleu",
;; "sgtu", and "sgeu" don't exist as regular instruction on the
;; mt, so these are not defined

;; Call and branch instructions

(define_expand "call"
  [(parallel [(call (mem:SI (match_operand:SI 0 "register_operand" ""))
			    (match_operand 1 "" ""))
	      (clobber (reg:SI 14))])]
  ""
  "
{
    operands[0] = force_reg (SImode, XEXP (operands[0], 0));
}")

(define_insn "call_internal"
  [(call (mem:SI (match_operand 0 "register_operand" "r"))
	 (match_operand 1 "" ""))
   ;; possibly add a clobber of the reg that gets the return address
   (clobber (reg:SI 14))]
  ""
  "jal r14, %0%#"
  [(set_attr "length" "4")
   (set_attr "type" "call")])

(define_expand "call_value"
  [(parallel [(set (match_operand 0 "register_operand" "")
		   (call (mem:SI (match_operand:SI 1 "register_operand" ""))
				 (match_operand 2 "general_operand" "")))
	      (clobber (reg:SI 14))])]
  ""
  "
{
    operands[1] = force_reg (SImode, XEXP (operands[1], 0));
}")


(define_insn "call_value_internal"
  [(set (match_operand 0 "register_operand" "=r")
	(call (mem:SI (match_operand 1 "register_operand" "r"))
	      (match_operand 2 "" "")))
	;; possibly add a clobber of the reg that gets the return address
	(clobber (reg:SI 14))]
  ""
  "jal r14, %1%#"
  [(set_attr "length" "4")
   (set_attr "type" "call")])

;; Subroutine return
(define_insn "return_internal"
  [(const_int 2)
   (return)
   (use (reg:SI 14))]
  ""
  "jal r0, r14%#"
  [(set_attr "length" "4")
   (set_attr "type" "call")])

;; Interrupt return
(define_insn "return_interrupt_internal"
  [(const_int 3)
   (return)
   (use (reg:SI 15))]
  ""
  "reti r15%#"
  [(set_attr "length" "4")
   (set_attr "type" "call")])

;; Subroutine return
(define_insn "eh_return_internal"
  [(return)
   (use (reg:SI 7))
   (use (reg:SI 8))
   (use (reg:SI 11))
   (use (reg:SI 10))]
  ""
  "jal r0, r11%#"
  [(set_attr "length" "4")
   (set_attr "type" "call")])


;; Normal unconditional jump
(define_insn "jump"
  [(set (pc) (label_ref (match_operand 0 "" "")))]
  ""
  "jmp %l0%#"
  [(set_attr "length" "4")
   (set_attr "type" "branch")])

;; Indirect jump through a register
(define_insn "indirect_jump"
  [(set (pc) (match_operand 0 "register_operand" "r"))]
  ""
  "jal r0,%0%#"
  [(set_attr "length" "4")
   (set_attr "type" "call")])

(define_insn "tablejump"
  [(set (pc) (match_operand:SI 0 "register_operand" "r"))
   (use (label_ref (match_operand 1 "" "")))]
  ""
  "jal r0, %0%#"
  [(set_attr "length" "4")
   (set_attr "type" "call")])


(define_expand "prologue"
  [(const_int 1)]
  ""
  "
{
  mt_expand_prologue ();
  DONE;
}")

(define_expand "epilogue"
  [(const_int 2)]
  ""
  "
{
  mt_expand_epilogue (NORMAL_EPILOGUE);
  DONE;
}")


(define_expand "eh_return"
  [(use (match_operand:SI 0 "register_operand" "r"))]
  ""
  "
{
  mt_expand_eh_return (operands);
  DONE;
}")


(define_insn_and_split "eh_epilogue"
  [(unspec [(match_operand 0 "register_operand" "r")] 6)]
  ""
  "#"
  "reload_completed"
  [(const_int 1)]
  "mt_emit_eh_epilogue (operands); DONE;"
)

;; No operation, needed in case the user uses -g but not -O.
(define_insn "nop"
  [(const_int 0)]
  ""
  "nop"
  [(set_attr "length" "4")
   (set_attr "type" "arith")])

;; ::::::::::::::::::::
;; ::
;; :: UNSPEC_VOLATILE usage
;; ::
;; ::::::::::::::::::::
;; 
;;	0	blockage
;;	1	Enable interrupts
;;	2	Disable interrupts
;;

;; Pseudo instruction that prevents the scheduler from moving code above this
;; point.
(define_insn "blockage"
  [(unspec_volatile [(const_int 0)] UNSPEC_BLOCKAGE)]
  ""
  ""
  [(set_attr "length" "0")])

;; Trap instruction to allow usage of the __builtin_trap function
(define_insn "trap"
  [(trap_if (const_int 1) (const_int 0))
   (clobber (reg:SI 14))]
  ""
  "si	r14%#"
  [(set_attr "length" "4")
   (set_attr "type" "branch")])

(define_expand "conditional_trap"
  [(trap_if (match_operator 0 "comparison_operator"
			    [(match_dup 2)
			     (match_dup 3)])
	    (match_operand 1 "const_int_operand" ""))]
  ""
  "
{
  operands[2] = mt_compare_op0;
  operands[3] = mt_compare_op1;
}")

;; Templates to control handling of interrupts

;; Enable interrupts template
(define_insn "ei"
  [(unspec_volatile [(const_int 0)] UNSPEC_EI)]
  ""
  "ei"
  [(set_attr "length" "4")])

;; Enable interrupts template
(define_insn "di"
  [(unspec_volatile [(const_int 0)] UNSPEC_DI)]
  ""
  "di"
  [(set_attr "length" "4")])
