;;  Machine Description for Renesas RX processors
;;  Copyright (C) 2008, 2009, 2010 Free Software Foundation, Inc.
;;  Contributed by Red Hat.

;; 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/>.


;; This code iterator allows all branch instructions to
;; be generated from a single define_expand template.
(define_code_iterator most_cond [eq ne gt ge lt le gtu geu ltu leu
				 unordered ordered ])

;; This code iterator is used for sign- and zero- extensions.
(define_mode_iterator small_int_modes [(HI "") (QI "")])

;; We do not handle DFmode here because it is either
;; the same as SFmode, or if -m64bit-doubles is active
;; then all operations on doubles have to be handled by
;; library functions.
(define_mode_iterator register_modes
  [(SF "ALLOW_RX_FPU_INSNS") (SI "") (HI "") (QI "")])


;; Used to map RX condition names to GCC
;; condition names for builtin instructions.
(define_code_iterator gcc_conds [eq ne gt ge lt le gtu geu ltu leu
				unge unlt uneq ltgt])
(define_code_attr rx_conds [(eq "eq") (ne "ne") (gt "gt") (ge "ge") (lt "lt")
			    (le "le") (gtu "gtu") (geu "geu") (ltu "ltu")
			    (leu "leu") (unge "pz") (unlt "n") (uneq "o")
			    (ltgt "no")])

(define_constants
  [
   (SP_REG 0)
   (CC_REG 		   16)

   (UNSPEC_LOW_REG         0)
   (UNSPEC_HIGH_REG        1)

   (UNSPEC_RTE             10)
   (UNSPEC_RTFI            11)
   (UNSPEC_NAKED           12)
   
   (UNSPEC_MOVSTR          20)
   (UNSPEC_MOVMEM          21)
   (UNSPEC_SETMEM          22)
   (UNSPEC_STRLEN          23)
   (UNSPEC_CMPSTRN         24)

   (UNSPEC_BUILTIN_BRK     30)
   (UNSPEC_BUILTIN_CLRPSW  31)
   (UNSPEC_BUILTIN_INT     32)
   (UNSPEC_BUILTIN_MACHI   33)
   (UNSPEC_BUILTIN_MACLO   34)
   (UNSPEC_BUILTIN_MULHI   35)
   (UNSPEC_BUILTIN_MULLO   36)
   (UNSPEC_BUILTIN_MVFACHI 37)
   (UNSPEC_BUILTIN_MVFACMI 38)
   (UNSPEC_BUILTIN_MVFC    39)
   (UNSPEC_BUILTIN_MVFCP   40)
   (UNSPEC_BUILTIN_MVTACHI 41)
   (UNSPEC_BUILTIN_MVTACLO 42)
   (UNSPEC_BUILTIN_MVTC    43)
   (UNSPEC_BUILTIN_MVTIPL  44)
   (UNSPEC_BUILTIN_RACW	   45)
   (UNSPEC_BUILTIN_REVW    46)
   (UNSPEC_BUILTIN_RMPA	   47)
   (UNSPEC_BUILTIN_ROUND   48)
   (UNSPEC_BUILTIN_SAT     49)
   (UNSPEC_BUILTIN_SETPSW  50)
   (UNSPEC_BUILTIN_WAIT	   51)
  ]
)

(define_attr "length" "" (const_int 8))

(include "predicates.md")
(include "constraints.md")

;; Pipeline description.

;; The RX only has a single pipeline.  It has five stages (fetch,
;; decode, execute, memory access, writeback) each of which normally
;; takes a single CPU clock cycle.

;; The timings attribute consists of two numbers, the first is the
;; throughput, which is the number of cycles the instruction takes
;; to execute and generate a result.  The second is the latency
;; which is the effective number of cycles the instruction takes to
;; execute if its result is used by the following instruction.  The
;; latency is always greater than or equal to the throughput.
;; These values were taken from tables 2.13 and 2.14 in section 2.8
;; of the RX610 Group Hardware Manual v0.11

;; Note - it would be nice to use strings rather than integers for
;; the possible values of this attribute, so that we can have the
;; gcc build mechanism check for values that are not supported by
;; the reservations below.  But this will not work because the code
;; in rx_adjust_sched_cost() needs integers not strings.

(define_attr "timings" "" (const_int 11))

(define_automaton "pipelining")
(define_cpu_unit "throughput" "pipelining")

(define_insn_reservation "throughput__1_latency__1"  1
  (eq_attr "timings" "11") "throughput")
(define_insn_reservation "throughput__1_latency__2"  2
  (eq_attr "timings" "12") "throughput,nothing")
(define_insn_reservation "throughput__2_latency__2"  1
  (eq_attr "timings" "22") "throughput*2")
(define_insn_reservation "throughput__3_latency__3"  1
  (eq_attr "timings" "33") "throughput*3")
(define_insn_reservation "throughput__3_latency__4"  2
  (eq_attr "timings" "34") "throughput*3,nothing")
(define_insn_reservation "throughput__4_latency__4"  1
  (eq_attr "timings" "44") "throughput*4")
(define_insn_reservation "throughput__4_latency__5"  2
  (eq_attr "timings" "45") "throughput*4,nothing")
(define_insn_reservation "throughput__5_latency__5"  1
  (eq_attr "timings" "55") "throughput*5")
(define_insn_reservation "throughput__5_latency__6"  2
  (eq_attr "timings" "56") "throughput*5,nothing")
(define_insn_reservation "throughput__6_latency__6"  1
  (eq_attr "timings" "66") "throughput*6")
(define_insn_reservation "throughput_10_latency_10"  1
  (eq_attr "timings" "1010") "throughput*10")
(define_insn_reservation "throughput_11_latency_11"  1
  (eq_attr "timings" "1111") "throughput*11")
(define_insn_reservation "throughput_16_latency_16"  1
  (eq_attr "timings" "1616") "throughput*16")
(define_insn_reservation "throughput_18_latency_18"  1
  (eq_attr "timings" "1818") "throughput*18")

;; Comparisons

;; Note - we do not specify the two instructions necessary to perform
;; a compare-and-branch in the cbranchsi4 pattern because that would
;; allow the comparison to be moved away from the jump before the reload
;; pass has completed.  That would be problematical because reload can
;; generate ADDSI3 instructions which would corrupt the PSW flags.

(define_expand "cbranchsi4"
  [(set (pc)
	(if_then_else (match_operator:SI 0 "comparison_operator"
					 [(match_operand:SI 1 "register_operand")
					  (match_operand:SI 2 "rx_source_operand")])
		      (label_ref (match_operand 3 ""))
		      (pc)))
   ]
  ""
  ""
)

(define_insn_and_split "*cbranchsi4_<code>"
  [(set (pc)
	(if_then_else (most_cond:SI (match_operand:SI  0 "register_operand"  "r")
				    (match_operand:SI  1 "rx_source_operand" "riQ"))
		      (label_ref (match_operand        2 "" ""))
		      (pc)))
   ]
  ""
  "#"
  "reload_completed"
  [(const_int 0)]
  "
  /* We contstruct the split by hand as otherwise the JUMP_LABEL
     attribute is not set correctly on the jump insn.  */
  emit_insn (gen_cmpsi (operands[0], operands[1]));
  
  emit_jump_insn (gen_conditional_branch (operands[2],
  		 gen_rtx_fmt_ee (<most_cond:CODE>, CCmode,
				 gen_rtx_REG (CCmode, CC_REG), const0_rtx)));
  "
)

(define_expand "cbranchsf4"
  [(set (pc)
	(if_then_else (match_operator:SF 0 "comparison_operator"
					 [(match_operand:SF 1 "register_operand")
					  (match_operand:SF 2 "rx_source_operand")])
		      (label_ref (match_operand 3 ""))
		      (pc)))
   ]
  "ALLOW_RX_FPU_INSNS"
  ""
)

(define_insn_and_split "*cbranchsf4_<code>"
  [(set (pc)
	(if_then_else (most_cond:SF (match_operand:SF  0 "register_operand"  "r")
				    (match_operand:SF  1 "rx_source_operand" "rFiQ"))
		      (label_ref (match_operand        2 "" ""))
		      (pc)))
   ]
  "ALLOW_RX_FPU_INSNS"
  "#"
  "&& reload_completed"
  [(const_int 0)]
  "
  /* We contstruct the split by hand as otherwise the JUMP_LABEL
     attribute is not set correctly on the jump insn.  */
  emit_insn (gen_cmpsf (operands[0], operands[1]));
  
  emit_jump_insn (gen_conditional_branch (operands[2],
  		 gen_rtx_fmt_ee (<most_cond:CODE>, CCmode,
 		 		 gen_rtx_REG (CCmode, CC_REG), const0_rtx)));
  "
)

(define_insn "tstsi"
  [(set (reg:CC_ZS CC_REG)
	(compare:CC_ZS (and:SI (match_operand:SI 0 "register_operand"  "r,r,r")
			       (match_operand:SI 1 "rx_source_operand" "r,i,Q"))
		       (const_int 0)))]
  ""
  {
    rx_float_compare_mode = false;
    return "tst\t%Q1, %0";
  }
  [(set_attr "timings" "11,11,33")
   (set_attr "length"   "3,7,6")]
)

(define_insn "cmpsi"
  [(set (reg:CC CC_REG)
	(compare:CC (match_operand:SI 0 "register_operand"  "r,r,r,r,r,r,r")
		    (match_operand:SI 1 "rx_source_operand" "r,Uint04,Int08,Sint16,Sint24,i,Q")))]
  ""
  {
    rx_float_compare_mode = false;
    if (rx_compare_redundant (insn))
      return "; Compare Eliminated: cmp %Q1, %0";
    return "cmp\t%Q1, %0";
  }
  [(set_attr "timings" "11,11,11,11,11,11,33")
   (set_attr "length"  "2,2,3,4,5,6,5")]
)

;; ??? g++.dg/eh/080514-1.C to see this happen.
(define_insn "cmpsf"
  [(set (reg:CC_ZSO CC_REG)
	(compare:CC_ZSO (match_operand:SF 0 "register_operand"  "r,r,r")
			(match_operand:SF 1 "rx_source_operand" "r,iF,Q")))]
  "ALLOW_RX_FPU_INSNS"
  {
    rx_float_compare_mode = true;
    return "fcmp\t%1, %0";
  }
  [(set_attr "timings" "11,11,33")
   (set_attr "length" "3,7,5")]
)

;; Flow Control Instructions:

(define_expand "b<code>"
  [(set (pc)
        (if_then_else (most_cond (reg:CC CC_REG) (const_int 0))
                      (label_ref (match_operand 0))
                      (pc)))]
  ""
  ""
)

(define_insn "conditional_branch"
  [(set (pc)
	(if_then_else (match_operator           1 "comparison_operator"
						[(reg:CC CC_REG) (const_int 0)])
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  {
    return rx_gen_cond_branch_template (operands[1], false);
  }
  [(set_attr "length" "8")    ;; This length is wrong, but it is
                              ;; too hard to compute statically.
   (set_attr "timings" "33")] ;; The timing assumes that the branch is taken.
)

(define_insn "*reveresed_conditional_branch"
  [(set (pc)
	(if_then_else (match_operator 1 "comparison_operator"
				      [(reg:CC CC_REG) (const_int 0)])
		      (pc)
		      (label_ref (match_operand 0 "" ""))))]
  ""
  {
    return rx_gen_cond_branch_template (operands[1], true);
  }
  [(set_attr "length" "8")    ;; This length is wrong, but it is
                              ;; too hard to compute statically.
   (set_attr "timings" "33")] ;; The timing assumes that the branch is taken.
)

(define_insn "jump"
  [(set (pc)
	(label_ref (match_operand 0 "" "")))]
  ""
  "bra\t%0"
  [(set_attr "length" "4")
   (set_attr "timings" "33")]
)

(define_insn "indirect_jump"
  [(set (pc)
	(match_operand:SI 0 "register_operand" "r"))]
  ""
  "jmp\t%0"
  [(set_attr "length" "2")
   (set_attr "timings" "33")]
)

(define_insn "tablejump"
  [(set (pc)
	(match_operand:SI          0 "register_operand" "r"))
   (use (label_ref (match_operand  1 "" "")))]
  ""
  { return flag_pic ? (TARGET_AS100_SYNTAX ? "\n?:\tbra\t%0"
					   : "\n1:\tbra\t%0")
	                                   : "jmp\t%0";
  }
  [(set_attr "timings" "33")
   (set_attr "length" "2")]
)

(define_insn "simple_return"
  [(return)]
  ""
  "rts"
  [(set_attr "length" "1")
   (set_attr "timings" "55")]
)

(define_insn "deallocate_and_return"
  [(set (reg:SI SP_REG)
	(plus:SI (reg:SI SP_REG)
		 (match_operand:SI 0 "immediate_operand" "i")))
   (return)]
  ""
  "rtsd\t%0"
  [(set_attr "length" "2")
   (set_attr "timings" "55")]
)

(define_insn "pop_and_return"
  [(match_parallel 1 "rx_rtsd_vector"
		   [(set:SI (reg:SI SP_REG)
			    (plus:SI (reg:SI SP_REG)
				     (match_operand:SI 0 "const_int_operand" "n")))])]
  "reload_completed"
  {
    rx_emit_stack_popm (operands, false);
    return "";
  }
  [(set_attr "length" "3")
   (set_attr "timings" "56")]
)

(define_insn "fast_interrupt_return"
  [(unspec_volatile [(return)] UNSPEC_RTFI) ]
  ""
  "rtfi"
  [(set_attr "length" "2")
   (set_attr "timings" "33")]
)

(define_insn "exception_return"
  [(unspec_volatile [(return)] UNSPEC_RTE) ]
  ""
  "rte"
  [(set_attr "length" "2")
   (set_attr "timings" "66")]
)

(define_insn "naked_return"
  [(unspec_volatile [(return)] UNSPEC_NAKED) ]
  ""
  "; Naked function: epilogue provided by programmer."
)


;; Note - the following set of patterns do not use the "memory_operand"
;; predicate or an "m" constraint because we do not allow symbol_refs
;; or label_refs as legitmate memory addresses.  This matches the
;; behaviour of most of the RX instructions.  Only the call/branch
;; instructions are allowed to refer to symbols/labels directly.
;; The call operands are in QImode because that is the value of
;; FUNCTION_MODE

(define_expand "call"
  [(call (match_operand:QI 0 "general_operand")
	 (match_operand:SI 1 "general_operand"))]
  ""
  {
    rtx dest = XEXP (operands[0], 0);

    if (! rx_call_operand (dest, Pmode))
      dest = force_reg (Pmode, dest);
    emit_call_insn (gen_call_internal (dest, operands[1]));
    DONE;
  }
)

(define_insn "call_internal"
  [(call (mem:QI (match_operand:SI 0 "rx_call_operand" "r,Symbol"))
	 (match_operand:SI         1 "general_operand" "g,g"))
   (clobber (reg:CC CC_REG))]
  ""
  "@
  jsr\t%0
  bsr\t%A0"
  [(set_attr "length" "2,4")
   (set_attr "timings" "33")]
)

(define_expand "call_value"
  [(set (match_operand          0 "register_operand")
	(call (match_operand:QI 1 "general_operand")
	      (match_operand:SI 2 "general_operand")))]
  ""
  {
    rtx dest = XEXP (operands[1], 0);

    if (! rx_call_operand (dest, Pmode))
      dest = force_reg (Pmode, dest);
    emit_call_insn (gen_call_value_internal (operands[0], dest, operands[2]));
    DONE;
  }
)

(define_insn "call_value_internal"
  [(set (match_operand                  0 "register_operand" "=r,r")
	(call (mem:QI (match_operand:SI 1 "rx_call_operand"   "r,Symbol"))
	      (match_operand:SI         2 "general_operand"   "g,g")))
   (clobber (reg:CC CC_REG))]
  ""
  "@
  jsr\t%1
  bsr\t%A1"
  [(set_attr "length" "2,4")
   (set_attr "timings" "33")]
)

;; Note - we do not allow indirect sibcalls (with the address
;; held in a register) because we cannot guarantee that the register
;; chosen will be a call-used one.  If it is a call-saved register,
;; then the epilogue code will corrupt it by popping the saved value
;; off of the stack.
(define_expand "sibcall"
  [(parallel
    [(call (mem:QI (match_operand:SI 0 "rx_symbolic_call_operand"))
	   (match_operand:SI         1 "general_operand"))
     (return)])]
  ""
  {
    if (MEM_P (operands[0]))
      operands[0] = XEXP (operands[0], 0);
  }
)

(define_insn "sibcall_internal"
  [(call (mem:QI (match_operand:SI 0 "rx_symbolic_call_operand" "Symbol"))
	 (match_operand:SI         1 "general_operand"          "g"))
   (return)]
  ""
  "bra\t%A0"
  [(set_attr "length"  "4")
   (set_attr "timings" "33")]
)

(define_expand "sibcall_value"
 [(parallel
   [(set (match_operand                  0 "register_operand")
	 (call (mem:QI (match_operand:SI 1 "rx_symbolic_call_operand"))
	       (match_operand:SI         2 "general_operand")))
    (return)])]
  ""
  {
    if (MEM_P (operands[1]))
      operands[1] = XEXP (operands[1], 0);
  }
)

(define_insn "sibcall_value_internal"
 [(set (match_operand                  0 "register_operand"         "=r")
       (call (mem:QI (match_operand:SI 1 "rx_symbolic_call_operand" "Symbol"))
	     (match_operand:SI         2 "general_operand"          "g")))
  (return)]
  ""
  "bra\t%A1"
  [(set_attr "length"  "4")
   (set_attr "timings" "33")]
)

;; Function Prologue/Epilogue Instructions

(define_expand "prologue"
  [(const_int 0)]
  ""
  "rx_expand_prologue (); DONE;"
)

(define_expand "epilogue"
  [(return)]
  ""
  "rx_expand_epilogue (false); DONE;"
)

(define_expand "sibcall_epilogue"
  [(return)]
  ""
  "rx_expand_epilogue (true); DONE;"
)

;; Move Instructions

;; Note - we do not allow memory to memory moves, even though the ISA
;; supports them.  The reason is that the conditions on such moves are
;; too restrictive, specifically the source addressing mode is limited
;; by the destination addressing mode and vice versa.  (For example it
;; is not possible to use indexed register indirect addressing for one
;; of the operands if the other operand is anything other than a register,
;; but it is possible to use register relative addressing when the other
;; operand also uses register relative or register indirect addressing).
;;
;; GCC does not support computing legitimate addresses based on the
;; nature of other operands involved in the instruction, and reload is
;; not smart enough to cope with a whole variety of different memory
;; addressing constraints, so it is simpler and safer to just refuse
;; to support memory to memory moves.

(define_expand "mov<register_modes:mode>"
  [(set (match_operand:register_modes 0 "general_operand")
	(match_operand:register_modes 1 "general_operand"))]
  ""
  {
    if (MEM_P (operand0) && MEM_P (operand1))
      operands[1] = copy_to_mode_reg (<register_modes:MODE>mode, operand1);
  }
)

(define_insn "*mov<register_modes:mode>_internal"
  [(set (match_operand:register_modes
	 0 "nonimmediate_operand" "=r,r,r,r,r,r,m,Q,Q,Q,Q")
	(match_operand:register_modes
	 1 "general_operand" "Int08,Sint16,Sint24,i,r,m,r,Int08,Sint16,Sint24,i"))]
  ""
  { return rx_gen_move_template (operands, false); }
  [(set_attr "length" "3,4,5,6,2,4,6,5,6,7,8")
   (set_attr "timings" "11,11,11,11,11,12,11,11,11,11,11")]
)

(define_insn "extend<small_int_modes:mode>si2"
  [(set (match_operand:SI 0 "register_operand"    "=r,r")
        (sign_extend:SI (match_operand:small_int_modes
			  1 "nonimmediate_operand" "r,m")))]
  ""
  { return rx_gen_move_template (operands, false); }
  [(set_attr "length" "2,6")
   (set_attr "timings" "11,12")]
)

(define_insn "zero_extend<small_int_modes:mode>si2"
  [(set (match_operand:SI 0 "register_operand"     "=r,r")
        (zero_extend:SI (match_operand:small_int_modes
			  1 "nonimmediate_operand"  "r,m")))]
  ""
  { return rx_gen_move_template (operands, true); }
  [(set_attr "length" "2,4")
   (set_attr "timings" "11,12")]
)

(define_insn "stack_push"
  [(set:SI (reg:SI SP_REG)
	   (minus:SI (reg:SI SP_REG)
		     (const_int 4)))
   (set:SI (mem:SI (reg:SI SP_REG))
	   (match_operand:SI 0 "register_operand" "r"))]
  ""
  "push.l\t%0"
  [(set_attr "length" "2")]
)

(define_insn "stack_pushm"
  [(match_parallel 1 "rx_store_multiple_vector"
		   [(set:SI (reg:SI SP_REG)
			    (minus:SI (reg:SI SP_REG)
				      (match_operand:SI 0 "const_int_operand" "n")))])]
  "reload_completed"
  {
    rx_emit_stack_pushm (operands);
    return "";
  }
  [(set_attr "length" "2")
   (set_attr "timings" "44")] ;; The timing is a guesstimate average timing.
)

(define_insn "stack_pop"
  [(set:SI (match_operand:SI 0 "register_operand" "=r")
	   (mem:SI (reg:SI SP_REG)))
   (set:SI (reg:SI SP_REG)
	   (plus:SI (reg:SI SP_REG)
		    (const_int 4)))]
  ""
  "pop\t%0"
  [(set_attr "length" "2")
   (set_attr "timings" "12")]
)

(define_insn "stack_popm"
  [(match_parallel 1 "rx_load_multiple_vector"
		   [(set:SI (reg:SI SP_REG)
			    (plus:SI (reg:SI SP_REG)
				     (match_operand:SI 0 "const_int_operand" "n")))])]
  "reload_completed"
  {
    rx_emit_stack_popm (operands, true);
    return "";
  }
  [(set_attr "length" "2")
   (set_attr "timings" "45")] ;; The timing is a guesstimate average timing.
)

;; FIXME: Add memory destination options ?
(define_insn "cstoresi4"
  [(set (match_operand:SI   0 "register_operand" "=r,r,r,r,r,r,r")
	(match_operator:SI  1 "comparison_operator"
	 [(match_operand:SI 2 "register_operand"  "r,r,r,r,r,r,r")
	  (match_operand:SI 3 "rx_source_operand" "r,Uint04,Int08,Sint16,Sint24,i,Q")]))
   (clobber (reg:CC CC_REG))] ;; Because the cc flags are set based on comparing ops 2 & 3 not the value in op 0.
  ""
  {
    rx_float_compare_mode = false;
    return "cmp\t%Q3, %Q2\n\tsc%B1.L\t%0";
  }
  [(set_attr "timings" "22,22,22,22,22,22,44")
   (set_attr "length"  "5,5,6,7,8,9,8")]
)

(define_expand "movsicc"
  [(parallel
    [(set (match_operand:SI                  0 "register_operand")
	  (if_then_else:SI (match_operand:SI 1 "comparison_operator")
			   (match_operand:SI 2 "nonmemory_operand")
			   (match_operand:SI 3 "immediate_operand")))
     (clobber (reg:CC CC_REG))])] ;; See cstoresi4
  ""
  {
    if (GET_CODE (operands[1]) != EQ && GET_CODE (operands[1]) != NE)
      FAIL;
    if (! CONST_INT_P (operands[3]))
      FAIL;
  }
)

(define_insn "*movsieq"
  [(set (match_operand:SI                      0 "register_operand" "=r,r,r")
	(if_then_else:SI (eq (match_operand:SI 3 "register_operand"  "r,r,r")
			     (match_operand:SI 4 "rx_source_operand" "riQ,riQ,riQ"))
			 (match_operand:SI     1 "nonmemory_operand" "0,i,r")
			 (match_operand:SI     2 "immediate_operand" "i,i,i")))
   (clobber (reg:CC CC_REG))] ;; See cstoresi4
  ""
  "@
  cmp\t%Q4, %Q3\n\tstnz\t%2, %0
  cmp\t%Q4, %Q3\n\tmov.l\t%2, %0\n\tstz\t%1, %0
  cmp\t%Q4, %Q3\n\tmov.l\t%1, %0\n\tstnz\t%2, %0"
  [(set_attr "length"  "13,19,15")
   (set_attr "timings" "22,33,33")]
)

(define_insn "*movsine"
  [(set (match_operand:SI                      0 "register_operand" "=r,r,r")
	(if_then_else:SI (ne (match_operand:SI 3 "register_operand"  "r,r,r")
			     (match_operand:SI 4 "rx_source_operand" "riQ,riQ,riQ"))
			 (match_operand:SI     1 "nonmemory_operand" "0,i,r")
			 (match_operand:SI     2 "immediate_operand" "i,i,i")))
   (clobber (reg:CC CC_REG))] ;; See cstoresi4
  ""
  "@
  cmp\t%Q4, %Q3\n\tstz\t%2, %0
  cmp\t%Q4, %Q3\n\tmov.l\t%2, %0\n\tstnz\t%1, %0
  cmp\t%Q4, %Q3\n\tmov.l\t%1, %0\n\tstz\t%2, %0"
  [(set_attr "length"  "13,19,15")
   (set_attr "timings" "22,33,33")]
)

;; Arithmetic Instructions

(define_insn "abssi2"
  [(set (match_operand:SI         0 "register_operand" "=r,r")
        (abs:SI (match_operand:SI 1 "register_operand"  "0,r")))
   (set (reg:CC_ZSO CC_REG)
	(compare:CC_ZSO (abs:SI (match_dup 1))
			(const_int 0)))]
  ""
  "@
  abs\t%0
  abs\t%1, %0"
  [(set_attr "length" "2,3")]
)

(define_insn "addsi3"
  [(set (match_operand:SI          0 "register_operand"  "=r,r,r,r,r,r,r,r,r,r,r,r,r,r")
	(plus:SI (match_operand:SI 1 "register_operand"  "%0,0,0,0,0,0,0,r,r,r,r,r,r,0")
		 (match_operand:SI 2 "rx_source_operand" "r,Uint04,NEGint4,Sint08,Sint16,Sint24,i,0,r,Sint08,Sint16,Sint24,i,Q")))
   (set (reg:CC_ZSC CC_REG) ;; See subsi3
	(compare:CC_ZSC (plus:SI (match_dup 1) (match_dup 2))
			(const_int 0)))]
  ""
  "@
  add\t%2, %0
  add\t%2, %0
  sub\t%N2, %0
  add\t%2, %0
  add\t%2, %0
  add\t%2, %0
  add\t%2, %0
  add\t%1, %0
  add\t%2, %1, %0
  add\t%2, %1, %0
  add\t%2, %1, %0
  add\t%2, %1, %0
  add\t%2, %1, %0
  add\t%Q2, %0"
  [(set_attr "timings" "11,11,11,11,11,11,11,11,11,11,11,11,11,33")
   (set_attr "length"   "2,2,2,3,4,5,6,2,3,3,4,5,6,5")]
)

(define_insn "adddi3"
  [(set (match_operand:DI          0 "register_operand" "=r,r,r,r,r,r")
	(plus:DI (match_operand:DI 1 "register_operand" "%0,0,0,0,0,0")
		 (match_operand:DI 2 "rx_source_operand"
				   "r,Sint08,Sint16,Sint24,i,Q")))
   (set (reg:CC_ZSC CC_REG) ;; See subsi3
	(compare:CC_ZSC (plus:DI (match_dup 1) (match_dup 2))
			(const_int 0)))]
  ""
  "add\t%L2, %L0\n\tadc\t%H2, %H0"
  [(set_attr "timings" "22,22,22,22,22,44")
   (set_attr "length" "5,7,9,11,13,11")]
)

(define_insn "andsi3"
  [(set (match_operand:SI         0 "register_operand"  "=r,r,r,r,r,r,r,r,r")
	(and:SI (match_operand:SI 1 "register_operand"  "%0,0,0,0,0,0,r,r,0")
		(match_operand:SI 2 "rx_source_operand" "r,Uint04,Sint08,Sint16,Sint24,i,0,r,Q")))
   (set (reg:CC_ZS CC_REG)
	(compare:CC_ZS (and:SI (match_dup 1) (match_dup 2))
		       (const_int 0)))]
  ""
  "@
  and\t%2, %0
  and\t%2, %0
  and\t%2, %0
  and\t%2, %0
  and\t%2, %0
  and\t%2, %0
  and\t%1, %0
  and\t%2, %1, %0
  and\t%Q2, %0"
  [(set_attr "timings" "11,11,11,11,11,11,11,33,33")
   (set_attr "length" "2,2,3,4,5,6,2,5,5")]
)

;; Byte swap (single 32-bit value).
(define_insn "bswapsi2"
  [(set (match_operand:SI           0 "register_operand" "+r")
	(bswap:SI (match_operand:SI 1 "register_operand"  "r")))]
  ""
  "revl\t%1, %0"
  [(set_attr "length" "3")]
)

;; Byte swap (single 16-bit value).  Note - we ignore the swapping of the high 16-bits.
(define_insn "bswaphi2"
  [(set (match_operand:HI           0 "register_operand" "+r")
	(bswap:HI (match_operand:HI 1 "register_operand"  "r")))]
  ""
  "revw\t%1, %0"
  [(set_attr "length" "3")]
)

(define_insn "divsi3"
  [(set (match_operand:SI         0 "register_operand" "=r,r,r,r,r,r")
	(div:SI (match_operand:SI 1 "register_operand"  "0,0,0,0,0,0")
		(match_operand:SI 2 "rx_source_operand" "r,Sint08,Sint16,Sint24,i,Q")))
   (clobber (reg:CC CC_REG))]
  ""
  "div\t%Q2, %0"
  [(set_attr "timings" "1111") ;; Strictly speaking the timing should be
                               ;; 2222, but that is a worst case sceanario.
   (set_attr "length" "3,4,5,6,7,6")]
)

(define_insn "udivsi3"
  [(set (match_operand:SI          0 "register_operand"  "=r,r,r,r,r,r")
	(udiv:SI (match_operand:SI 1 "register_operand"   "0,0,0,0,0,0")
		 (match_operand:SI 2 "rx_source_operand"  "r,Sint08,Sint16,Sint24,i,Q")))
   (clobber (reg:CC CC_REG))]
  ""
  "divu\t%Q2, %0"
  [(set_attr "timings" "1010") ;; Strictly speaking the timing should be
                               ;; 2020, but that is a worst case sceanario.
   (set_attr "length" "3,4,5,6,7,6")]
)

;; Note - these patterns are suppressed in big-endian mode because they
;; generate a little endian result.  ie the most significant word of the
;; result is placed in the higher numbered register of the destination
;; register pair.

(define_insn "mulsidi3"
  [(set (match_operand:DI          0 "register_operand"  "=r,r,r,r,r,r")
        (mult:DI (sign_extend:DI (match_operand:SI
				  1 "register_operand"  "%0,0,0,0,0,0"))
                 (sign_extend:DI (match_operand:SI
				  2 "rx_source_operand"
				  "r,Sint08,Sint16,Sint24,i,Q"))))]
  "! TARGET_BIG_ENDIAN_DATA"
  "emul\t%Q2, %0"
  [(set_attr "length" "3,4,5,6,7,6")   
   (set_attr "timings" "22,22,22,22,22,44")]
)

;; See comment for mulsidi3.
;; Note - the zero_extends are to distinguish this pattern from the
;; mulsidi3 pattern.  Immediate mode addressing is not supported
;; because gcc cannot handle the expression: (zero_extend (const_int)).
(define_insn "umulsidi3"
  [(set (match_operand:DI                          0 "register_operand"	 "=r,r")
        (mult:DI (zero_extend:DI (match_operand:SI 1 "register_operand"  "%0,0"))
                 (zero_extend:DI (match_operand:SI 2 "rx_compare_operand" "r,Q"))))]
  "! TARGET_BIG_ENDIAN_DATA"
  "emulu\t%Q2, %0"
  [(set_attr "length" "3,6")
   (set_attr "timings" "22,44")]
)

(define_insn "smaxsi3"
  [(set (match_operand:SI          0 "register_operand" "=r,r,r,r,r,r")
	(smax:SI (match_operand:SI 1 "register_operand" "%0,0,0,0,0,0")
		 (match_operand:SI 2 "rx_source_operand"
				   "r,Sint08,Sint16,Sint24,i,Q")))]
  ""
  "max\t%Q2, %0"
  [(set_attr "length" "3,4,5,6,7,6")
   (set_attr "timings" "11,11,11,11,11,33")]
)

(define_insn "sminsi3"
  [(set (match_operand:SI          0 "register_operand" "=r,r,r,r,r,r")
	(smin:SI (match_operand:SI 1 "register_operand" "%0,0,0,0,0,0")
		 (match_operand:SI 2 "rx_source_operand"
				   "r,Sint08,Sint16,Sint24,i,Q")))]
  ""
  "min\t%Q2, %0"
  [(set_attr "length"  "3,4,5,6,7,6")
   (set_attr "timings" "11,11,11,11,11,33")]
)

(define_insn "mulsi3"
  [(set (match_operand:SI          0 "register_operand" "=r,r,r,r,r,r,r,r,r")
        (mult:SI (match_operand:SI 1 "register_operand" "%0,0,0,0,0,0,0,r,r")
                 (match_operand:SI 2 "rx_source_operand"
				   "r,Uint04,Sint08,Sint16,Sint24,i,Q,0,r")))]
  ""
  "@
  mul\t%2, %0
  mul\t%2, %0
  mul\t%2, %0
  mul\t%2, %0
  mul\t%2, %0
  mul\t%Q2, %0
  mul\t%Q2, %0
  mul\t%1, %0
  mul\t%2, %1, %0"
  [(set_attr "length"  "2,2,3,4,5,6,5,2,3")
   (set_attr "timings" "11,11,11,11,11,11,33,11,11")]
)

(define_insn "negsi2"
  [(set (match_operand:SI         0 "register_operand" "=r,r")
        (neg:SI (match_operand:SI 1 "register_operand"  "0,r")))
   (set (reg:CC CC_REG)
	(compare:CC (neg:SI (match_dup 1))
		    (const_int 0)))]
  ;; The NEG instruction does not comply with -fwrapv semantics.
  ;; See gcc.c-torture/execute/pr22493-1.c for an example of this.
  "! flag_wrapv"
  "@
  neg\t%0
  neg\t%1, %0"
  [(set_attr "length" "2,3")]
)

(define_insn "one_cmplsi2"
  [(set (match_operand:SI         0 "register_operand" "=r,r")
	(not:SI (match_operand:SI 1 "register_operand"  "0,r")))
   (set (reg:CC_ZS CC_REG)
	(compare:CC_ZS (not:SI (match_dup 1))
		       (const_int 0)))]
  ""
  "@
  not\t%0
  not\t%1, %0"
  [(set_attr "length" "2,3")]
)

(define_insn "iorsi3"
  [(set (match_operand:SI         0 "register_operand" "=r,r,r,r,r,r,r,r,r")
	(ior:SI (match_operand:SI 1 "register_operand" "%0,0,0,0,0,0,r,r,0")
	        (match_operand:SI 2 "rx_source_operand" "r,Uint04,Sint08,Sint16,Sint24,i,0,r,Q")))
   (set (reg:CC_ZS CC_REG)
	(compare:CC_ZS (ior:SI (match_dup 1) (match_dup 2))
		       (const_int 0)))]
  ""
  "@
  or\t%2, %0
  or\t%2, %0
  or\t%2, %0
  or\t%2, %0
  or\t%2, %0
  or\t%Q2, %0
  or\t%1, %0
  or\t%2, %1, %0
  or\t%Q2, %0"
  [(set_attr "timings" "11,11,11,11,11,11,11,11,33")
   (set_attr "length"  "2,2,3,4,5,6,2,3,5")]
)

(define_insn "rotlsi3"
  [(set (match_operand:SI            0 "register_operand" "=r")
	(rotate:SI (match_operand:SI 1 "register_operand"  "0")
		   (match_operand:SI 2 "rx_shift_operand" "rn")))
   (set (reg:CC_ZS CC_REG)
	(compare:CC_ZS (rotate:SI (match_dup 1) (match_dup 2))
		       (const_int 0)))]
  ""
  "rotl\t%2, %0"
  [(set_attr "length" "3")]
)

(define_insn "rotrsi3"
  [(set (match_operand:SI              0 "register_operand" "=r")
	(rotatert:SI (match_operand:SI 1 "register_operand"  "0")
		     (match_operand:SI 2 "rx_shift_operand" "rn")))
   (set (reg:CC_ZS CC_REG)
	(compare:CC_ZS (rotatert:SI (match_dup 1) (match_dup 2))
		       (const_int 0)))]
  ""
  "rotr\t%2, %0"
  [(set_attr "length" "3")]
)

(define_insn "ashrsi3"
  [(set (match_operand:SI              0 "register_operand" "=r,r,r")
	(ashiftrt:SI (match_operand:SI 1 "register_operand"  "0,0,r")
		     (match_operand:SI 2 "rx_shift_operand"  "r,n,n")))
   (set (reg:CC_ZS CC_REG)
	(compare:CC_ZS (ashiftrt:SI (match_dup 1) (match_dup 2))
		       (const_int 0)))]
  ""
  "@
  shar\t%2, %0
  shar\t%2, %0
  shar\t%2, %1, %0"
  [(set_attr "length" "3,2,3")]
)

(define_insn "lshrsi3"
  [(set (match_operand:SI              0 "register_operand" "=r,r,r")
	(lshiftrt:SI (match_operand:SI 1 "register_operand"  "0,0,r")
		     (match_operand:SI 2 "rx_shift_operand"  "r,n,n")))
   (set (reg:CC_ZS CC_REG)
	(compare:CC_ZS (lshiftrt:SI (match_dup 1) (match_dup 2))
		       (const_int 0)))]
  ""
  "@
  shlr\t%2, %0
  shlr\t%2, %0
  shlr\t%2, %1, %0"
  [(set_attr "length" "3,2,3")]
)

(define_insn "ashlsi3"
  [(set (match_operand:SI            0 "register_operand" "=r,r,r")
	(ashift:SI (match_operand:SI 1 "register_operand"  "0,0,r")
	           (match_operand:SI 2 "rx_shift_operand"  "r,n,n")))
   (set (reg:CC_ZS CC_REG)
	(compare:CC_ZS (ashift:SI (match_dup 1) (match_dup 2))
		       (const_int 0)))]
  ""
  "@
  shll\t%2, %0
  shll\t%2, %0
  shll\t%2, %1, %0"
  [(set_attr "length" "3,2,3")]
)

(define_insn "subsi3"
  [(set (match_operand:SI           0 "register_operand" "=r,r,r,r,r")
	(minus:SI (match_operand:SI 1 "register_operand"  "0,0,0,r,0")
		  (match_operand:SI 2 "rx_source_operand" "r,Uint04,n,r,Q")))
   (set (reg:CC_ZSC CC_REG)
	;; Note - we do not acknowledge that the SUB instruction sets the Overflow
	;; flag because its interpretation is different from comparing the result
	;; against zero.  Compile and run gcc.c-torture/execute/cmpsi-1.c to see this.
	(compare:CC_ZSC (minus:SI (match_dup 1) (match_dup 2))
			(const_int 0)))]
  ""
  "@
  sub\t%2, %0
  sub\t%2, %0
  add\t%N2, %0
  sub\t%2, %1, %0
  sub\t%Q2, %0"
  [(set_attr "timings" "11,11,11,11,33")
   (set_attr "length" "2,2,6,3,5")]
)

(define_insn "subdi3"
  [(set (match_operand:DI           0 "register_operand" "=r,r")
	(minus:DI (match_operand:DI 1 "register_operand"  "0,0")
		  (match_operand:DI 2 "rx_source_operand" "r,Q")))
   (set (reg:CC_ZSC CC_REG) ;; See subsi3
	(compare:CC_ZSC (minus:DI (match_dup 1) (match_dup 2))
			(const_int 0)))]
  ""
  "sub\t%L2, %L0\n\tsbb\t%H2, %H0"
  [(set_attr "timings" "22,44")
   (set_attr "length" "5,11")]
)

(define_insn "xorsi3"
  [(set (match_operand:SI         0 "register_operand" "=r,r,r,r,r,r")
	(xor:SI (match_operand:SI 1 "register_operand" "%0,0,0,0,0,0")
	        (match_operand:SI 2 "rx_source_operand"
				  "r,Sint08,Sint16,Sint24,i,Q")))
   (set (reg:CC_ZS CC_REG)
	(compare:CC_ZS (xor:SI (match_dup 1) (match_dup 2))
		       (const_int 0)))]
  ""
  "xor\t%Q2, %0"
  [(set_attr "timings" "11,11,11,11,11,33")
   (set_attr "length" "3,4,5,6,7,6")]
)

;; Floating Point Instructions

(define_insn "addsf3"
  [(set (match_operand:SF          0 "register_operand"  "=r,r,r")
	(plus:SF (match_operand:SF 1 "register_operand"  "%0,0,0")
		 (match_operand:SF 2 "rx_source_operand"  "r,F,Q")))
   (set (reg:CC_ZS CC_REG)
	(compare:CC_ZS (plus:SF (match_dup 1) (match_dup 2))
			(const_int 0)))]
  "ALLOW_RX_FPU_INSNS"
  "fadd\t%2, %0"
  [(set_attr "timings" "44,44,66")
   (set_attr "length" "3,7,5")]
)

(define_insn "divsf3"
  [(set (match_operand:SF         0 "register_operand" "=r,r,r")
	(div:SF (match_operand:SF 1 "register_operand"  "0,0,0")
		(match_operand:SF 2 "rx_source_operand" "r,F,Q")))
   (set (reg:CC_ZS CC_REG)
	(compare:CC_ZS (div:SF (match_dup 1) (match_dup 2))
			(const_int 0)))]
  "ALLOW_RX_FPU_INSNS"
  "fdiv\t%2, %0"
  [(set_attr "timings" "1616,1616,1818")
   (set_attr "length" "3,7,5")]
)

(define_insn "mulsf3"
  [(set (match_operand:SF          0 "register_operand" "=r,r,r")
	(mult:SF (match_operand:SF 1 "register_operand" "%0,0,0")
		(match_operand:SF  2 "rx_source_operand" "r,F,Q")))
   (set (reg:CC_ZS CC_REG)
	(compare:CC_ZS (mult:SF (match_dup 1) (match_dup 2))
			(const_int 0)))]
  "ALLOW_RX_FPU_INSNS"
  "fmul\t%2, %0"
  [(set_attr "timings" "33,33,55")
   (set_attr "length"  "3,7,5")]
)

(define_insn "subsf3"
  [(set (match_operand:SF           0 "register_operand" "=r,r,r")
	(minus:SF (match_operand:SF 1 "register_operand"  "0,0,0")
		  (match_operand:SF 2 "rx_source_operand" "r,F,Q")))
   (set (reg:CC_ZS CC_REG)
	(compare:CC_ZS (minus:SF (match_dup 1) (match_dup 2))
		       (const_int 0)))]
  "ALLOW_RX_FPU_INSNS"
  "fsub\t%Q2, %0"
  [(set_attr "timings" "44,44,66")
   (set_attr "length" "3,7,5")]
)

(define_insn "fix_truncsfsi2"
  [(set (match_operand:SI         0 "register_operand"  "=r,r")
	(fix:SI (match_operand:SF 1 "rx_compare_operand" "r,Q")))
   (set (reg:CC_ZS CC_REG)
	(compare:CC_ZS (fix:SI (match_dup 1))
		       (const_int 0)))]
  "ALLOW_RX_FPU_INSNS"
  "ftoi\t%Q1, %0"
  [(set_attr "timings" "22,44")
   (set_attr "length" "3,5")]
)

(define_insn "floatsisf2"
  [(set (match_operand:SF           0 "register_operand"  "=r,r")
	(float:SF (match_operand:SI 1 "rx_compare_operand" "r,Q")))
   (set (reg:CC_ZS CC_REG)
	(compare:CC_ZS (float:SF (match_dup 1))
			(const_int 0)))]
  "ALLOW_RX_FPU_INSNS"
  "itof\t%Q1, %0"
  [(set_attr "timings" "22,44")
   (set_attr "length" "3,6")]
)

;; Bit manipulation instructions.
;; Note - there are two versions of each pattern because the memory
;; accessing versions use QImode whilst the register accessing
;; versions use SImode.
;; The peephole are here because the combiner only looks at a maximum
;; of three instructions at a time.

(define_insn "bitset"
  [(set:SI (match_operand:SI                    0 "register_operand" "=r")
	   (ior:SI (match_operand:SI            1 "register_operand" "0")
		   (ashift:SI (const_int 1)
			      (match_operand:SI 2 "nonmemory_operand" "ri"))))]
  ""
  "bset\t%2, %0"
  [(set_attr "length" "3")]
)

(define_insn "bitset_in_memory"
  [(set:QI (match_operand:QI                    0 "memory_operand" "=m")
	   (ior:QI (match_operand:QI            1 "memory_operand" "0")
		   (ashift:QI (const_int 1)
			      (match_operand:QI 2 "nonmemory_operand" "ri"))))]
  ""
  "bset\t%2, %0.B"
  [(set_attr "length" "3")
   (set_attr "timings" "34")]
)

;; (set (reg A) (const_int 1))
;; (set (reg A) (ashift (reg A) (reg B)))
;; (set (reg C) (ior (reg A) (reg C)))
(define_peephole2
  [(set:SI (match_operand:SI 0 "register_operand" "")
	   (const_int 1))
   (set:SI (match_dup 0)
	   (ashift:SI (match_dup 0)
		      (match_operand:SI 1 "register_operand" "")))
   (set:SI (match_operand:SI 2 "register_operand" "")
	   (ior:SI (match_dup 0)
		   (match_dup 2)))]
  "dead_or_set_p (insn, operands[0])"
  [(set:SI (match_dup 2)
	   (ior:SI (match_dup 2)
		   (ashift:SI (const_int 1)
			      (match_dup 1))))]
)
  
;; (set (reg A) (const_int 1))
;; (set (reg A) (ashift (reg A) (reg B)))
;; (set (reg A) (ior (reg A) (reg C)))
;; (set (reg C) (reg A)
(define_peephole2
  [(set:SI (match_operand:SI 0 "register_operand" "")
	   (const_int 1))
   (set:SI (match_dup 0)
	   (ashift:SI (match_dup 0)
		      (match_operand:SI 1 "register_operand" "")))
   (set:SI (match_dup 0)
	   (ior:SI (match_dup 0)
		   (match_operand:SI 2 "register_operand" "")))
   (set:SI (match_dup 2) (match_dup 0))]
  "dead_or_set_p (insn, operands[0])"
  [(set:SI (match_dup 2)
	   (ior:SI (match_dup 2)
		   (ashift:SI (const_int 1)
			      (match_dup 1))))]
)
  
(define_insn "bitinvert"
  [(set:SI (match_operand:SI 0 "register_operand" "+r")
	   (xor:SI (match_operand:SI 1 "register_operand" "0")
		   (ashift:SI (const_int 1)
			      (match_operand:SI 2 "nonmemory_operand" "ri"))))]
  ""
  "bnot\t%2, %0"
  [(set_attr "length" "3")]
)

(define_insn "bitinvert_in_memory"
  [(set:QI (match_operand:QI 0 "memory_operand" "+m")
	   (xor:QI (match_operand:QI 1 "register_operand" "0")
		   (ashift:QI (const_int 1)
			      (match_operand:QI 2 "nonmemory_operand" "ri"))))]
  ""
  "bnot\t%2, %0.B"
  [(set_attr "length" "5")
   (set_attr "timings" "33")]
)

;; (set (reg A) (const_int 1))
;; (set (reg A) (ashift (reg A) (reg B)))
;; (set (reg C) (xor (reg A) (reg C)))
(define_peephole2
  [(set:SI (match_operand:SI 0 "register_operand" "")
	   (const_int 1))
   (set:SI (match_dup 0)
	   (ashift:SI (match_dup 0)
		      (match_operand:SI 1 "register_operand" "")))
   (set:SI (match_operand:SI 2 "register_operand" "")
	   (xor:SI (match_dup 0)
		   (match_dup 2)))]
  "dead_or_set_p (insn, operands[0])"
  [(set:SI (match_dup 2)
	   (xor:SI (match_dup 2)
		   (ashift:SI (const_int 1)
			      (match_dup 1))))]
  ""
)
  
;; (set (reg A) (const_int 1))
;; (set (reg A) (ashift (reg A) (reg B)))
;; (set (reg A) (xor (reg A) (reg C)))
;; (set (reg C) (reg A))
(define_peephole2
  [(set:SI (match_operand:SI 0 "register_operand" "")
	   (const_int 1))
   (set:SI (match_dup 0)
	   (ashift:SI (match_dup 0)
		      (match_operand:SI 1 "register_operand" "")))
   (set:SI (match_dup 0)
	   (xor:SI (match_dup 0)
		   (match_operand:SI 2 "register_operand" "")))
   (set:SI (match_dup 2) (match_dup 0))]
  "dead_or_set_p (insn, operands[0])"
  [(set:SI (match_dup 2)
	   (xor:SI (match_dup 2)
		   (ashift:SI (const_int 1)
			      (match_dup 1))))]
  ""
)

(define_insn "bitclr"
  [(set:SI (match_operand:SI 0 "register_operand" "+r")
	   (and:SI (match_operand:SI 1 "register_operand" "0")
		   (not:SI (ashift:SI (const_int 1)
				      (match_operand:SI 2 "nonmemory_operand" "ri")))))]
  ""
  "bclr\t%2, %0"
  [(set_attr "length" "3")]
)

(define_insn "bitclr_in_memory"
  [(set:QI (match_operand:QI 0 "memory_operand" "+m")
	   (and:QI (match_operand:QI 1 "memory_operand" "0")
		   (not:QI (ashift:QI (const_int 1)
				      (match_operand:QI 2 "nonmemory_operand" "ri")))))]
  ""
  "bclr\t%2, %0.B"
  [(set_attr "length" "3")
   (set_attr "timings" "34")]
)

;; (set (reg A) (const_int -2))
;; (set (reg A) (rotate (reg A) (reg B)))
;; (set (reg C) (and (reg A) (reg C)))
(define_peephole2
  [(set:SI (match_operand:SI 0 "register_operand" "")
	   (const_int -2))
   (set:SI (match_dup 0)
	   (rotate:SI (match_dup 0)
		      (match_operand:SI 1 "register_operand" "")))
   (set:SI (match_operand:SI 2 "register_operand" "")
	   (and:SI (match_dup 0)
		   (match_dup 2)))]
  "dead_or_set_p (insn, operands[0])"
  [(set:SI (match_dup 2)
	   (and:SI (match_dup 2)
		   (not:SI (ashift:SI (const_int 1)
				      (match_dup 1)))))]
)
  
;; (set (reg A) (const_int -2))
;; (set (reg A) (rotate (reg A) (reg B)))
;; (set (reg A) (and (reg A) (reg C)))
;; (set (reg C) (reg A)
(define_peephole2
  [(set:SI (match_operand:SI 0 "register_operand" "")
	   (const_int -2))
   (set:SI (match_dup 0)
	   (rotate:SI (match_dup 0)
		      (match_operand:SI 1 "register_operand" "")))
   (set:SI (match_dup 0)
	   (and:SI (match_dup 0)
		   (match_operand:SI 2 "register_operand" "")))
   (set:SI (match_dup 2) (match_dup 0))]
  "dead_or_set_p (insn, operands[0])"
  [(set:SI (match_dup 2)
	   (and:SI (match_dup 2)
		   (not:SI (ashift:SI (const_int 1)
				      (match_dup 1)))))]
)

(define_expand "insv"
  [(set:SI (zero_extract:SI (match_operand:SI 0 "nonimmediate_operand") ;; Destination
		            (match_operand    1 "immediate_operand")    ;; # of bits to set
			    (match_operand    2 "immediate_operand"))   ;; Starting bit
	   (match_operand	              3 "immediate_operand"))]  ;; Bits to insert
  ""
  {
    if (rx_expand_insv (operands))
      DONE;
    FAIL;
  }
)   

;; Atomic exchange operation.

(define_insn "sync_lock_test_and_setsi"
  [(set:SI (match_operand:SI 0 "register_operand"   "=r,r")
	   (match_operand:SI 1 "rx_compare_operand" "=r,Q"))
   (set:SI (match_dup 1)
	   (match_operand:SI 2 "register_operand"    "0,0"))]
  ""
  "xchg\t%1, %0"
  [(set_attr "length" "3,6")
   (set_attr "timings" "22")]
)

;; Block move functions.

(define_expand "movstr"
  [(set:SI (match_operand:BLK 1 "memory_operand")    ;; Dest
	   (match_operand:BLK 2 "memory_operand"))   ;; Source
   (use (match_operand:SI     0 "register_operand")) ;; Updated Dest
  ]
  ""
  {
    rtx addr1 = gen_rtx_REG (SImode, 1);
    rtx addr2 = gen_rtx_REG (SImode, 2);
    rtx len   = gen_rtx_REG (SImode, 3);
    rtx dest_copy = gen_reg_rtx (SImode);

    emit_move_insn (len, GEN_INT (-1));
    emit_move_insn (addr1, force_operand (XEXP (operands[1], 0), NULL_RTX));
    emit_move_insn (addr2, force_operand (XEXP (operands[2], 0), NULL_RTX));
    operands[1] = replace_equiv_address_nv (operands[1], addr1);
    operands[2] = replace_equiv_address_nv (operands[2], addr2);
    emit_move_insn (dest_copy, addr1);
    emit_insn (gen_rx_movstr ());
    emit_move_insn (len, GEN_INT (-1));
    emit_insn (gen_rx_strend (operands[0], dest_copy));
    DONE;
  }
)

(define_insn "rx_movstr"
  [(set:SI (mem:BLK (reg:SI 1))
	   (mem:BLK (reg:SI 2)))
   (unspec_volatile:BLK [(reg:SI 1) (reg:SI 2) (reg:SI 3)] UNSPEC_MOVSTR)
   (clobber (reg:SI 1))
   (clobber (reg:SI 2))
   (clobber (reg:SI 3))]
  ""
  "smovu"
  [(set_attr "length" "2")
   (set_attr "timings" "1111")] ;; The timing is a guesstimate.
)

(define_insn "rx_strend"
  [(set:SI (match_operand:SI                      0 "register_operand" "=r")
	   (unspec_volatile:SI [(match_operand:SI 1 "register_operand"  "r")
				(reg:SI 3)] UNSPEC_STRLEN))
   (clobber (reg:SI 1))
   (clobber (reg:SI 2))
   (clobber (reg:SI 3))
   (clobber (reg:CC CC_REG))
   ]
  ""
  "mov\t%1, r1\n\tmov\t#0, r2\n\tsuntil.b\n\tmov\tr1, %0\n\tsub\t#1, %0"
  [(set_attr "length" "10")
   (set_attr "timings" "1111")] ;; The timing is a guesstimate.
)

(define_expand "movmemsi"
  [(parallel
    [(set (match_operand:BLK 0 "memory_operand")    ;; Dest
	  (match_operand:BLK 1 "memory_operand"))   ;; Source
     (use (match_operand:SI  2 "register_operand")) ;; Length in bytes
     (match_operand          3 "immediate_operand") ;; Align
     (unspec_volatile:BLK [(reg:SI 1) (reg:SI 2) (reg:SI 3)] UNSPEC_MOVMEM)]
    )]
  ""
  {
    rtx addr1 = gen_rtx_REG (SImode, 1);
    rtx addr2 = gen_rtx_REG (SImode, 2);
    rtx len   = gen_rtx_REG (SImode, 3);

    if (REG_P (operands[0]) && (REGNO (operands[0]) == 2
				      || REGNO (operands[0]) == 3))
      FAIL;
    if (REG_P (operands[1]) && (REGNO (operands[1]) == 1
				      || REGNO (operands[1]) == 3))
      FAIL;
    if (REG_P (operands[2]) && (REGNO (operands[2]) == 1
				      || REGNO (operands[2]) == 2))
      FAIL;
    emit_move_insn (addr1, force_operand (XEXP (operands[0], 0), NULL_RTX));
    emit_move_insn (addr2, force_operand (XEXP (operands[1], 0), NULL_RTX));
    emit_move_insn (len, force_operand (operands[2], NULL_RTX));
    operands[0] = replace_equiv_address_nv (operands[0], addr1);
    operands[1] = replace_equiv_address_nv (operands[1], addr2);
    emit_insn (gen_rx_movmem ());
    DONE;
  }
)

(define_insn "rx_movmem"
  [(set (mem:BLK (reg:SI 1))
	(mem:BLK (reg:SI 2)))
   (use (reg:SI 3))
   (unspec_volatile:BLK [(reg:SI 1) (reg:SI 2) (reg:SI 3)] UNSPEC_MOVMEM)
   (clobber (reg:SI 1))
   (clobber (reg:SI 2))
   (clobber (reg:SI 3))]
  ""
  "smovf"
  [(set_attr "length" "2")
   (set_attr "timings" "1111")] ;; The timing is a guesstimate.
)

(define_expand "setmemsi"
  [(set (match_operand:BLK 0 "memory_operand")     ;; Dest
        (match_operand:QI  2 "nonmemory_operand")) ;; Value
   (use (match_operand:SI  1 "nonmemory_operand")) ;; Length
   (match_operand          3 "immediate_operand")  ;; Align
   (unspec_volatile:BLK [(reg:SI 1) (reg:SI 2) (reg:SI 3)] UNSPEC_SETMEM)]
  ""
  {
    rtx addr = gen_rtx_REG (SImode, 1);
    rtx val  = gen_rtx_REG (QImode, 2);
    rtx len  = gen_rtx_REG (SImode, 3);

    emit_move_insn (addr, force_operand (XEXP (operands[0], 0), NULL_RTX));
    emit_move_insn (len, force_operand (operands[1], NULL_RTX));
    emit_move_insn (val, operands[2]);
    emit_insn (gen_rx_setmem ());
    DONE;
  }
)

(define_insn "rx_setmem"
  [(set:BLK (mem:BLK (reg:SI 1)) (reg 2))
   (unspec_volatile:BLK [(reg:SI 1) (reg:SI 2) (reg:SI 3)] UNSPEC_SETMEM)
   (clobber (reg:SI 1))
   (clobber (reg:SI 3))]
  ""
  "sstr.b"
  [(set_attr "length" "2")
   (set_attr "timings" "1111")] ;; The timing is a guesstimate.
)

(define_expand "cmpstrnsi"
  [(set (match_operand:SI                       0 "register_operand")   ;; Result
	(unspec_volatile:SI [(match_operand:BLK 1 "memory_operand")     ;; String1
			     (match_operand:BLK 2 "memory_operand")]    ;; String2
			    UNSPEC_CMPSTRN))
   (use (match_operand:SI                       3 "register_operand"))  ;; Max Length
   (match_operand:SI                            4 "immediate_operand")] ;; Known Align
  ""
  {
    rtx str1 = gen_rtx_REG (SImode, 1);
    rtx str2 = gen_rtx_REG (SImode, 2);
    rtx len  = gen_rtx_REG (SImode, 3);
  
    emit_move_insn (str1, force_operand (XEXP (operands[1], 0), NULL_RTX));
    emit_move_insn (str2, force_operand (XEXP (operands[2], 0), NULL_RTX));
    emit_move_insn (len, force_operand (operands[3], NULL_RTX));

    emit_insn (gen_rx_cmpstrn (operands[0], operands[1], operands[2]));
    DONE;
  }
)

(define_expand "cmpstrsi"
  [(set (match_operand:SI                       0 "register_operand")   ;; Result
	(unspec_volatile:SI [(match_operand:BLK 1 "memory_operand")     ;; String1
			     (match_operand:BLK 2 "memory_operand")]    ;; String2
			    UNSPEC_CMPSTRN))
   (match_operand:SI                            3 "immediate_operand")] ;; Known Align
  ""
  {
    rtx str1 = gen_rtx_REG (SImode, 1);
    rtx str2 = gen_rtx_REG (SImode, 2);
    rtx len  = gen_rtx_REG (SImode, 3);
  
    emit_move_insn (str1, force_reg (SImode, XEXP (operands[1], 0)));
    emit_move_insn (str2, force_reg (SImode, XEXP (operands[2], 0)));
    emit_move_insn (len, GEN_INT (-1));

    emit_insn (gen_rx_cmpstrn (operands[0], operands[1], operands[2]));
    DONE;
  }
)

(define_insn "rx_cmpstrn"
  [(set:SI (match_operand:SI 0 "register_operand" "=r")
	   (unspec_volatile:SI [(reg:SI 1) (reg:SI 2) (reg:SI 3)]
			       UNSPEC_CMPSTRN))
   (use (match_operand:BLK   1 "memory_operand" "m"))
   (use (match_operand:BLK   2 "memory_operand" "m"))
   (clobber (reg:SI 1))
   (clobber (reg:SI 2))
   (clobber (reg:SI 3))
   (clobber (reg:CC CC_REG))]
  ""
  "scmpu		; Perform the string comparison
   mov     #-1, %0      ; Set up -1 result (which cannot be created
                        ; by the SC insn)
   bnc	   ?+		; If Carry is not set skip over
   scne.L  %0		; Set result based on Z flag
?:              	
"
  [(set_attr "length" "9")
   (set_attr "timings" "1111")] ;; The timing is a guesstimate.
)

;;   Builtin Functions
;;
;; GCC does not have the ability to generate the following instructions
;; on its own so they are provided as builtins instead.  To use them from
;; a program for example invoke them as __builtin_rx_<insn_name>.  For
;; example:
;;
;;    int short_byte_swap (int arg) { return __builtin_rx_revw (arg); }

;;---------- Accumulator Support ------------------------

;; Multiply & Accumulate (high)
(define_insn "machi"
  [(unspec:SI [(match_operand:SI 0 "register_operand" "r")
	       (match_operand:SI 1 "register_operand" "r")]
	      UNSPEC_BUILTIN_MACHI)]
  ""
  "machi\t%0, %1"
  [(set_attr "length" "3")]
)

;; Multiply & Accumulate (low)
(define_insn "maclo"
  [(unspec:SI [(match_operand:SI 0 "register_operand" "r")
	       (match_operand:SI 1 "register_operand" "r")]
	      UNSPEC_BUILTIN_MACLO)]
  ""
  "maclo\t%0, %1"
  [(set_attr "length" "3")]
)

;; Multiply (high)
(define_insn "mulhi"
  [(unspec:SI [(match_operand:SI 0 "register_operand" "r")
	       (match_operand:SI 1 "register_operand" "r")]
	      UNSPEC_BUILTIN_MULHI)]
  ""
  "mulhi\t%0, %1"
  [(set_attr "length" "3")]
)

;; Multiply (low)
(define_insn "mullo"
  [(unspec:SI [(match_operand:SI 0 "register_operand" "r")
	       (match_operand:SI 1 "register_operand" "r")]
	      UNSPEC_BUILTIN_MULLO)]
  ""
  "mullo\t%0, %1"
  [(set_attr "length" "3")]
)

;; Move from Accumulator (high)
(define_insn "mvfachi"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(unspec:SI [(const_int 0)]
		   UNSPEC_BUILTIN_MVFACHI))]
  ""
  "mvfachi\t%0"
  [(set_attr "length" "3")]
)

;; Move from Accumulator (middle)
(define_insn "mvfacmi"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(unspec:SI [(const_int 0)]
		   UNSPEC_BUILTIN_MVFACMI))]
  ""
  "mvfacmi\t%0"
  [(set_attr "length" "3")]
)

;; Move to Accumulator (high)
(define_insn "mvtachi"
  [(unspec_volatile:SI [(match_operand:SI 0 "register_operand" "r")]
		       UNSPEC_BUILTIN_MVTACHI)]
  ""
  "mvtachi\t%0"
  [(set_attr "length" "3")]
)

;; Move to Accumulator (low)
(define_insn "mvtaclo"
  [(unspec_volatile:SI [(match_operand:SI 0 "register_operand" "r")]
		       UNSPEC_BUILTIN_MVTACLO)]
  ""
  "mvtaclo\t%0"
  [(set_attr "length" "3")]
)

;; Round Accumulator
(define_insn "racw"
  [(unspec_volatile:SI [(match_operand:SI 0 "immediate_operand" "i")]
		       UNSPEC_BUILTIN_RACW)]
  ""
  "racw\t%0"
  [(set_attr "length" "3")]
)

;; Repeat multiply and accumulate
(define_insn "rmpa"
  [(unspec:SI [(const_int 0) (reg:SI 1) (reg:SI 2) (reg:SI 3)
	       (reg:SI 4) (reg:SI 5) (reg:SI 6)]
	      UNSPEC_BUILTIN_RMPA)
  (clobber (reg:SI 1))
  (clobber (reg:SI 2))
  (clobber (reg:SI 3))]
  ""
  "rmpa"
  [(set_attr "length" "2")
   (set_attr "timings" "1010")]
)

;;---------- Arithmetic ------------------------

;; Byte swap (two 16-bit values).
(define_insn "revw"
  [(set (match_operand:SI             0 "register_operand" "+r")
	(unspec:SI [(match_operand:SI 1 "register_operand"  "r")]
		   UNSPEC_BUILTIN_REVW))]
  ""
  "revw\t%1, %0"
  [(set_attr "length" "3")]
)

;; Round to integer.
(define_insn "lrintsf2"
  [(set (match_operand:SI             0 "register_operand"  "=r,r")
	(unspec:SI [(match_operand:SF 1 "rx_compare_operand" "r,Q")]
		   UNSPEC_BUILTIN_ROUND))
   (clobber (reg:CC CC_REG))]
  ""
  "round\t%1, %0"
  [(set_attr "timings" "22,44")   
   (set_attr "length" "3,5")]
)

;; Saturate to 32-bits
(define_insn "sat"
  [(set (match_operand:SI             0 "register_operand" "=r")
	(unspec:SI [(match_operand:SI 1 "register_operand"  "0")]
		   UNSPEC_BUILTIN_SAT))]
  ""
  "sat\t%0"
  [(set_attr "length" "2")]
)

;;---------- Control Registers ------------------------

;; Clear Processor Status Word
(define_insn "clrpsw"
  [(unspec:SI [(match_operand:SI 0 "immediate_operand" "i")]
	      UNSPEC_BUILTIN_CLRPSW)
   (clobber (reg:CC CC_REG))]
  ""
  "clrpsw\t%F0"
  [(set_attr "length" "2")]
)

;; Set Processor Status Word
(define_insn "setpsw"
  [(unspec:SI [(match_operand:SI 0 "immediate_operand" "i")]
	      UNSPEC_BUILTIN_SETPSW)
   (clobber (reg:CC CC_REG))]
  ""
  "setpsw\t%F0"
  [(set_attr "length" "2")]
)

;; Move from control register
(define_insn "mvfc"
  [(set (match_operand:SI             0 "register_operand" "=r")
	(unspec:SI [(match_operand:SI 1 "immediate_operand" "i")]
		   UNSPEC_BUILTIN_MVFC))]
  ""
  "mvfc\t%C1, %0"
  [(set_attr "length" "3")]
)

;; Move to control register
(define_insn "mvtc"
  [(unspec:SI [(match_operand:SI 0 "immediate_operand" "i,i")
	       (match_operand:SI 1 "nonmemory_operand" "r,i")]
	      UNSPEC_BUILTIN_MVTC)]
  ""
  "mvtc\t%1, %C0"
  [(set_attr "length" "3,7")]
  ;; Ignore possible clobbering of the comparison flags in the
  ;; PSW register.  This is a cc0 target so any cc0 setting
  ;; instruction will always be paired with a cc0 user, without
  ;; the possibility of this instruction being placed in between
  ;; them.
)

;; Move to interrupt priority level
(define_insn "mvtipl"
  [(unspec:SI [(match_operand:SI 0 "immediate_operand" "Uint04")]
	      UNSPEC_BUILTIN_MVTIPL)]
  ""
  "mvtipl\t%0"
  [(set_attr "length" "3")]
)

;;---------- Interrupts ------------------------

;; Break
(define_insn "brk"
  [(unspec_volatile [(const_int 0)]
		    UNSPEC_BUILTIN_BRK)]
  ""
  "brk"
  [(set_attr "length" "1")
   (set_attr "timings" "66")]
)

;; Interrupt
(define_insn "int"
  [(unspec_volatile:SI [(match_operand:SI 0 "immediate_operand" "i")]
		       UNSPEC_BUILTIN_INT)]
  ""
  "int\t%0"
  [(set_attr "length" "3")]
)

;; Wait
(define_insn "wait"
  [(unspec_volatile [(const_int 0)]
		    UNSPEC_BUILTIN_WAIT)]
  ""
  "wait"
  [(set_attr "length" "2")]
)

;;---------- CoProcessor Support ------------------------

;; FIXME: The instructions are currently commented out because
;; the bit patterns have not been finalized, so the assembler
;; does not support them.  Once they are decided and the assembler
;; supports them, enable the instructions here.

;; Move from co-processor register
(define_insn "mvfcp"
  [(set (match_operand:SI             0 "register_operand" "=r")
	(unspec:SI [(match_operand:SI 1 "immediate_operand" "i")
		    (match_operand:SI 2 "immediate_operand" "i")]
		   UNSPEC_BUILTIN_MVFCP))]
  ""
  "; mvfcp\t%1, %0, %2"
  [(set_attr "length" "5")]
)

;;---------- Misc ------------------------

;; Required by cfglayout.c...
(define_insn "nop"
  [(const_int 0)]
  ""
  "nop"
  [(set_attr "length" "1")]
)
