;; Machine description of the Adaptiva epiphany cpu for GNU C compiler
;; Copyright (C) 1994-2013 Free Software Foundation, Inc.
;; Contributed by Embecosm on behalf of Adapteva, 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/>.

;; See file "rtl.def" for documentation on define_insn, match_*, et. al.

(define_constants
  [(GPR_0			 0)
   (GPR_FP			11)
   (GPR_IP			12)
   (GPR_SP			13)
   (GPR_LR			14)
   (GPR_16			16)
   (GPR_18			18)
   (GPR_20			20)
   (ARG_POINTER_REGNUM		64)
   (FRAME_POINTER_REGNUM	65)
   (CC_REGNUM			66)   ;; 66 or 17
   (CCFP_REGNUM			67)   ;; 67 or 18
   (CONFIG_REGNUM		68)
   (STATUS_REGNUM		69)
   (LC_REGNUM			70)
   (LS_REGNUM			71)
   (LE_REGNUM			72)
   (IRET_REGNUM			73)
   (FP_NEAREST_REGNUM		74)
   (FP_TRUNCATE_REGNUM		75)
   (FP_ANYFP_REGNUM		76)
   (UNKNOWN_REGNUM		77) ; used for addsi3_r and friends
   ; We represent the return address as an unspec rather than a reg.
   ; If we used a reg, we could use register elimination, but eliminating
   ; to GPR_LR would make the latter visible to dataflow, thus making it
   ; harder to determine when it must be saved.
   (UNSPEC_RETURN_ADDR		 0)
   (UNSPEC_FP_MODE		 1)

   (UNSPECV_GID			 0)
   (UNSPECV_GIE			 1)])

;; Insn type.  Used to default other attribute values.

(define_attr "type"
  "move,load,store,cmove,unary,compare,shift,mul,uncond_branch,branch,call,fp,fp_int,v2fp,misc,sfunc,fp_sfunc,flow"
  (const_string "misc"))

;; Length (in # bytes)

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

;; The length here is the length of a single asm.

(define_asm_attributes
  [(set_attr "length" "4")
   (set_attr "type" "misc")])

;; pipeline model; so far we have only one.
(define_attr "pipe_model" "epiphany" (const_string "epiphany"))

(define_attr "rounding" "trunc,nearest"
  (cond [(ne (symbol_ref "TARGET_ROUND_NEAREST") (const_int 0))
	 (const_string "nearest")]
	(const_string "trunc")))

(define_attr "fp_mode" "round_unknown,round_nearest,round_trunc,int,caller,none"
  (cond [(eq_attr "type" "fp,v2fp,fp_sfunc")
	 (symbol_ref "(enum attr_fp_mode) epiphany_normal_fp_rounding")
	 (eq_attr "type" "call")
	 (symbol_ref "(enum attr_fp_mode) epiphany_normal_fp_mode")
	 (eq_attr "type" "fp_int")
	 (const_string "int")]
	(const_string "none")))

(include "epiphany-sched.md")

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

;; modes that are held in a single register, and hence, a word.
(define_mode_iterator WMODE [SI SF HI QI V2HI V4QI])
(define_mode_iterator WMODE2 [SI SF HI QI V2HI V4QI])

;; modes that are held in a two single registers
(define_mode_iterator DWMODE [DI DF V2SI V2SF V4HI V8QI])

;; Double-word mode made up of two single-word mode values.
(define_mode_iterator DWV2MODE [V2SI V2SF])
(define_mode_attr vmode_part [(V2SI "si") (V2SF "sf")])
(define_mode_attr vmode_PART [(V2SI "SI") (V2SF "SF")])
(define_mode_attr vmode_fp_type [(V2SI "fp_int") (V2SF "fp")])
(define_mode_attr vmode_ccmode [(V2SI "CC") (V2SF "CC_FP")])
(define_mode_attr vmode_cc [(V2SI "CC_REGNUM") (V2SF "CCFP_REGNUM")])

;; Move instructions.

(define_expand "mov<mode>"
  [(set (match_operand:WMODE 0 "general_operand" "")
	(match_operand:WMODE 1 "general_operand" ""))]
  ""
{
  if (<MODE>mode == V4QImode || <MODE>mode == V2HImode)
    {
      operands[0] = simplify_gen_subreg (SImode, operands[0], <MODE>mode, 0);
      operands[1] = simplify_gen_subreg (SImode, operands[1], <MODE>mode, 0);
      emit_insn (gen_movsi (operands[0], operands[1]));
      DONE;
    }
  if (GET_CODE (operands[0]) == MEM)
    operands[1] = force_reg (<MODE>mode, operands[1]);
  if (<MODE>mode == SImode
      && (operands[1] == frame_pointer_rtx || operands[1] == arg_pointer_rtx))
    {
      rtx reg = operands[0];

      if (!REG_P (reg))
	reg = gen_reg_rtx (SImode);
      emit_insn (gen_move_frame (reg, operands[1]));
      operands[1] = reg;
      if (operands[0] == reg)
	DONE;
    }
})

(define_insn "*movqi_insn"
  [(set (match_operand:QI 0 "move_dest_operand" "=Rcs,   r,  r,r,m")
	(match_operand:QI 1 "move_src_operand"   "Rcs,rU16,Cal,m,r"))]
;; ??? Needed?
  "gpr_operand (operands[0], QImode)
   || gpr_operand (operands[1], QImode)"
  "@
   mov %0,%1
   mov %0,%1
   mov %0,%1
   ldrb %0,%1
   strb %1,%0"
  [(set_attr "type" "move,move,move,load,store")])

(define_insn_and_split "*movhi_insn"
  [(set (match_operand:HI 0 "move_dest_operand" "=r,  r,r,m")
	(match_operand:HI 1 "move_src_operand""rU16,Cal,m,r"))]
  "gpr_operand (operands[0], HImode)
   || gpr_operand (operands[1], HImode)"
  "@
   mov %0,%1
   mov %0,%%low(%1); %1
   ldrh %0,%c1
   strh %1,%c0"
  "reload_completed && CONSTANT_P (operands[1])
   && !satisfies_constraint_U16 (operands[1]) && TARGET_SPLIT_LOHI"
  [(set (match_dup 2) (match_dup 3))]
  "operands[2] = simplify_gen_subreg (SImode, operands[0], HImode, 0);
   operands[3] = simplify_gen_subreg (SImode, operands[1], HImode, 0);"
  [(set_attr "type" "move,move,load,store")])

;; We use a special pattern for a move from the frame pointer to
;; show the flag clobber that is needed when this move is changed
;; to an add by register elimination.
;; ??? A pseudo register might be equivalent to a function invariant,
;; and thus placed by reload into reg_equiv_invariant; if the pseudo
;; does not get a hard register, we then end up with the function
;; invariant in its place, i.e. an unexpected clobber of the flags
;; register.
;;
;; N.B. operand 1 is an operand so that reload will perform elimination.
;;
;; The post-reload pattern recognition and splitting is done in frame_move_1.
(define_insn "move_frame"
  [(set (match_operand:SI 0 "gpr_operand" "=r")
	(match_operand:SI 1 "register_operand" "r"))
   (clobber (reg:CC CC_REGNUM))]
  "operands[1] == frame_pointer_rtx || operands[1] == arg_pointer_rtx"
  "#")

(define_insn "movsi_high"
  [(set (match_operand:SI 0 "gpr_operand" "+r")
	(ior:SI (and:SI (match_dup 0) (const_int 65535))
		(high:SI (match_operand:SI 1 "move_src_operand" "i"))))]
  ""
  "movt %0, %%high(%1)"
  [(set_attr "type" "move")
   (set_attr "length" "4")])

(define_insn "movsi_lo_sum"
  [(set (match_operand:SI 0 "gpr_operand" "=r")
	(lo_sum:SI (const_int 0)
		   (match_operand:SI 1 "move_src_operand" "i")))]
  ""
  "mov %0, %%low(%1)"
  [(set_attr "type" "move")
   (set_attr "length" "4")])

(define_insn_and_split "*movsi_insn"
  [(set (match_operand:SI 0 "move_dest_operand"
	 "=   r,  r,  r,  r,  r,   r,   m,  r,  Rct")
	(match_operand:SI 1 "move_src_operand"
	 "rU16Rra,Cm1,Cl1,Cr1,Cal,mSra,rRra,Rct,r"))]
  "gpr_operand (operands[0], SImode)
   || gpr_operand (operands[1], SImode)
   || satisfies_constraint_Sra (operands[1])"
{
  switch (which_alternative)
    {
    case 0: return "mov %0,%1";
    case 1: return "add %0,%-,(1+%1)";
    case 2: operands[1] = GEN_INT (exact_log2 (-INTVAL (operands[1])));
      return "lsl %0,%-,%1";
    case 3: operands[1] = GEN_INT (32 - exact_log2 (INTVAL (operands[1]) + 1));
      return "lsr %0,%-,%1";
    case 4: return "mov %0,%%low(%1)\;movt %0,%%high(%1) ; %1";
    case 5: return "ldr %0,%C1";
    case 6: return "str %1,%C0";
    case 7: return "movfs %0,%1";
    case 8: return "movts %0,%1";
    default: gcc_unreachable ();
    }
}
  "reload_completed && CONSTANT_P (operands[1])
   && !satisfies_constraint_U16 (operands[1])
   && !satisfies_constraint_Cm1 (operands[1])
   && !satisfies_constraint_Cl1 (operands[1])
   && !satisfies_constraint_Cr1 (operands[1])
   && TARGET_SPLIT_LOHI"
  [(match_dup 2) (match_dup 3)]
  "operands[2] = gen_movsi_lo_sum (operands[0], operands[1]);
   operands[3] = gen_movsi_high (operands[0], operands[1]);"
  [(set_attr "type" "move,misc,misc,misc,move,load,store,flow,flow")
   (set_attr "length" "4,4,4,4,8,4,4,4,4")])

(define_split
  [(set (match_operand:SI 0 "nonimmediate_operand")
	(unspec:SI [(const_int 0)] UNSPEC_RETURN_ADDR))]
  "reload_completed && !MACHINE_FUNCTION (cfun)->lr_clobbered"
  [(set (match_dup 0) (reg:SI GPR_LR))])

(define_split
  [(set (match_operand:SI 0 "gpr_operand")
	(unspec:SI [(const_int 0)] UNSPEC_RETURN_ADDR))]
  "reload_completed"
  [(set (match_dup 0) (match_dup 1))]
{
  emit_insn (gen_reload_insi_ra (operands[0], operands[1]));
  DONE;
})

(define_expand "reload_insi_ra"
  [(set (match_operand:SI 0 "gpr_operand" "r") (match_operand:SI 1 "" "Sra"))]
  ""
{
  rtx addr
    = (frame_pointer_needed ? hard_frame_pointer_rtx : stack_pointer_rtx);

  addr = plus_constant (Pmode, addr, MACHINE_FUNCTION (cfun)->lr_slot_offset);
  operands[1] = gen_frame_mem (SImode, addr);
})

;; If the frame pointer elimination offset is zero, we'll use this pattern.
;; Note that the splitter can accept any gpr in operands[1]; this is
;; necessary, (e.g. for compile/20021015-1.c -O0,)
;; because when register elimination cannot be done with the constant
;; as an immediate operand of the add instruction, reload will resort to
;; loading the constant into a reload register, using gen_add2_insn to add
;; the stack pointer, and then use the reload register as new source in
;; the move_frame pattern.
(define_insn_and_split "*move_frame_1"
  [(set (match_operand:SI 0 "gpr_operand" "=r")
	(match_operand:SI 1 "gpr_operand" "r"))
   (clobber (reg:CC CC_REGNUM))]
  "(reload_in_progress || reload_completed)
   && (operands[1] == stack_pointer_rtx
       || operands[1] == hard_frame_pointer_rtx)"
  "#"
  "reload_in_progress || reload_completed"
  [(set (match_dup 0) (match_dup 1))])

(define_expand "mov<mode>"
  [(set (match_operand:DWMODE 0 "general_operand" "")
	(match_operand:DWMODE 1 "general_operand" ""))]
  ""
  "
{
  if (GET_MODE_CLASS (<MODE>mode) == MODE_VECTOR_INT
      || GET_MODE_CLASS (<MODE>mode) == MODE_VECTOR_FLOAT)
    {
      if (epiphany_vect_align == 4 && TARGET_SPLIT_VECMOVE_EARLY)
	{
	  rtx o0l, o0h, o1l, o1h;

	  o0l = simplify_gen_subreg (SImode, operands[0], <MODE>mode, 0);
	  o0h = simplify_gen_subreg (SImode, operands[0], <MODE>mode,
				     UNITS_PER_WORD);
	  o1l = simplify_gen_subreg (SImode, operands[1], <MODE>mode, 0);
	  o1h = simplify_gen_subreg (SImode, operands[1], <MODE>mode,
				     UNITS_PER_WORD);
	  if (reg_overlap_mentioned_p (o0l, o1h))
	    {
	      emit_move_insn (o0h, o1h);
	      emit_move_insn (o0l, o1l);
	    }
	  else
	    {
	      emit_move_insn (o0l, o1l);
	      emit_move_insn (o0h, o1h);
	    }
	  DONE;
	}
      /* lower_subreg has a tendency to muck up vectorized code.
	 To protect the wide memory accesses, we must use same-size
	 subregs.  */
      if (epiphany_vect_align != 4 /* == 8 */
	  && !reload_in_progress
	  && (GET_CODE (operands[0]) == MEM || GET_CODE (operands[1]) == MEM)
	  && !misaligned_operand (operands[1], <MODE>mode)
	  && (GET_CODE (operands[0]) != SUBREG
	      || (GET_MODE_SIZE (GET_MODE (SUBREG_REG (operands[0])))
		  != GET_MODE_SIZE (<MODE>mode)
		  && GET_CODE (operands[1]) != SUBREG)))
	{
	  operands[0]
	    = simplify_gen_subreg (DImode, operands[0], <MODE>mode, 0);
	  operands[1]
	    = simplify_gen_subreg (DImode, operands[1], <MODE>mode, 0);
	  emit_insn (gen_movdi (operands[0], operands[1]));
	  DONE;
	}
    }
  /* Everything except mem = const or mem = mem can be done easily.  */

  if (GET_CODE (operands[0]) == MEM)
    operands[1] = force_reg (<MODE>mode, operands[1]);
}")

(define_insn_and_split "*mov<mode>_insn"
  [(set (match_operand:DWMODE 0 "move_dest_operand"      "=r,   r,r,m")
	(match_operand:DWMODE 1 "move_double_src_operand" "r,CalE,m,r"))]
  "(gpr_operand (operands[0], <MODE>mode)
    || gpr_operand (operands[1], <MODE>mode))"
  "@
   #
   #
   ldrd %0,%X1
   strd %1,%X0"
  "reload_completed
   && (((!MEM_P (operands[0]) || misaligned_operand (operands[0], <MODE>mode))
	&& (!MEM_P (operands[1])
	    || misaligned_operand (operands[1], <MODE>mode)))
       || epiphany_vect_align == 4)"
  [(set (match_dup 2) (match_dup 3))
   (set (match_dup 4) (match_dup 5))]
{
  int word0 = 0, word1 = UNITS_PER_WORD;

  if (post_modify_operand (operands[0], <MODE>mode)
      || post_modify_operand (operands[1], <MODE>mode))
    word0 = UNITS_PER_WORD, word1 = 0;

  operands[2] = simplify_gen_subreg (SImode, operands[0], <MODE>mode, word0);
  operands[3] = simplify_gen_subreg (SImode, operands[1], <MODE>mode, word0);
  operands[4] = simplify_gen_subreg (SImode, operands[0], <MODE>mode, word1);
  operands[5] = simplify_gen_subreg (SImode, operands[1], <MODE>mode, word1);
  if (post_modify_operand (operands[0], <MODE>mode))
    operands[2]
      = change_address (operands[2], VOIDmode,
			plus_constant (Pmode, XEXP (XEXP (operands[0], 0), 0),
				       UNITS_PER_WORD));
  if (post_modify_operand (operands[1], <MODE>mode))
    operands[3]
      = change_address (operands[3], VOIDmode,
			plus_constant (Pmode, XEXP (XEXP (operands[1], 0), 0),
				       UNITS_PER_WORD));
}
  [(set_attr "type" "move,move,load,store")
   (set_attr "length" "8,16,4,4")])


(define_insn_and_split "*movsf_insn"
  [(set (match_operand:SF 0 "move_dest_operand" "=r,r,r,m")
	(match_operand:SF 1 "move_src_operand"   "r,E,m,r"))]
  "gpr_operand (operands[0], SFmode)
   || gpr_operand (operands[1], SFmode)"
  "@
   mov %0,%1
   mov %0,%%low(%1)\;movt %0,%%high(%1) ; %1
   ldr %0,%C1
   str %1,%C0"
  "reload_completed && CONSTANT_P (operands[1]) && TARGET_SPLIT_LOHI"
  [(set (match_dup 2) (match_dup 3))]
  "operands[2] = simplify_gen_subreg (SImode, operands[0], SFmode, 0);
   operands[3] = simplify_gen_subreg (SImode, operands[1], SFmode, 0);"
  [(set_attr "type" "move,move,load,store")
   (set_attr "length" "4,8,4,4")])

(define_expand "addsi3"
  [(set (match_operand:SI 0 "add_reg_operand" "")
	(plus:SI (match_operand:SI 1 "add_reg_operand" "")
		 (match_operand:SI 2 "add_operand" "")))]
  ""
  "
{
  if (reload_in_progress || reload_completed)
    emit_insn (gen_addsi3_r (operands[0], operands[1], operands[2]));
  else if (TARGET_FP_IARITH && add_reg_operand (operands[2], SImode))
    emit_insn (gen_iadd (operands[0], operands[1], operands[2]));
  else
    emit_insn (gen_addsi3_i (operands[0], operands[1], operands[2]));
  DONE;
}")

(define_insn "addsi3_i"
  [(set (match_operand:SI 0 "add_reg_operand" "=r")
	(plus:SI (match_operand:SI 1 "add_reg_operand" "%r")
		 (match_operand:SI 2 "add_operand" "rL")))
   (clobber (reg:CC CC_REGNUM))]
  ""
  "add %0,%1,%2"
[(set_attr "type" "misc")])

; We use a clobber of UNKNOWN_REGNUM here so that the peephole optimizers
; can identify the unresolved flags clobber problem, and also to
; avoid unwanted matches.
;
; At -O0 / -O1 we don't peephole all instances away.  We could get better
; debug unwinding through the emitted code if we added a splitter.
(define_insn "addsi3_r"
  [(set (match_operand:SI 0 "gpr_operand" "=r")
	(plus:SI (match_operand:SI 1 "gpr_operand" "%r")
		 (match_operand:SI 2 "nonmemory_operand" "rCar")))
   (clobber (reg:CC UNKNOWN_REGNUM))]
  "reload_in_progress || reload_completed"
{
  int scratch = (0x17
		 ^ (true_regnum (operands[0]) & 1)
		 ^ (true_regnum (operands[1]) & 2)
		 ^ (true_regnum (operands[2]) & 4));
  asm_fprintf (asm_out_file, "\tstr r%d,[sp,#0]\n", scratch);
  asm_fprintf (asm_out_file, "\tmovfs r%d,status\n", scratch);
  output_asm_insn ("add %0,%1,%2", operands);
  asm_fprintf (asm_out_file, "\tmovts status,r%d\n", scratch);
  asm_fprintf (asm_out_file, "\tldr r%d,[sp,#0]\n", scratch);
  return "";
}
  [(set_attr "length" "20")
   (set_attr "type" "misc")])

;; reload uses gen_addsi2 because it doesn't understand the need for
;; the clobber.
(define_peephole2
  [(set (match_operand:SI 0 "gpr_operand" "")
	(match_operand:SI 1 "const_int_operand" ""))
   (parallel [(set (match_dup 0)
		   (plus:SI (match_dup 0)
			    (match_operand:SI 2 "gpr_operand")))
	      (clobber (reg:CC UNKNOWN_REGNUM))])]
  "satisfies_constraint_L (operands[1])
   || ((operands[2] == stack_pointer_rtx
	|| (operands[2] == hard_frame_pointer_rtx && frame_pointer_needed))
       && !peep2_regno_dead_p (2, CC_REGNUM)
       && satisfies_constraint_Car (operands[1]))"
  [(parallel [(set (match_dup 0)
		   (plus:SI (match_dup 2) (match_dup 1)))
	      (clobber (reg:CC UNKNOWN_REGNUM))])]
  ;; FIXME:
  ;; need this patch: http://gcc.gnu.org/ml/gcc-patches/2011-10/msg02819.html
  ;; "peep2_rescan = true;"
)

(define_peephole2
  [(match_parallel 5 ""
     [(set (match_operand 3 "cc_operand" "") (match_operand 4 "" ""))])
   (parallel [(set (match_operand:SI 0 "gpr_operand" "")
		   (plus:SI (match_operand:SI 1 "gpr_operand" "")
			    (match_operand:SI 2 "nonmemory_operand" "")))
	      (clobber (reg:CC UNKNOWN_REGNUM))])]
  "REGNO (operands[3]) == CC_REGNUM
   && (gpr_operand (operands[2], SImode)
       || satisfies_constraint_L (operands[2]))
   && !reg_overlap_mentioned_p (operands[0], operands[5])
   && !reg_set_p (operands[1], operands[5])
   && !reg_set_p (operands[2], operands[5])"
  [(parallel [(set (match_operand:SI 0 "gpr_operand" "")
		   (plus:SI (match_operand:SI 1 "gpr_operand" "")
			    (match_operand:SI 2 "nonmemory_operand" "")))
	      (clobber (reg:CC CC_REGNUM))])
   (match_dup 5)]
  "")

(define_peephole2
  [(parallel [(set (match_operand:SI 0 "gpr_operand" "")
		   (plus:SI (match_operand:SI 1 "gpr_operand" "")
			    (match_operand:SI 2 "nonmemory_operand" "")))
	      (clobber (reg:CC UNKNOWN_REGNUM))])]
  "peep2_regno_dead_p (1, CC_REGNUM)
   && (gpr_operand (operands[2], SImode)
       || satisfies_constraint_L (operands[2]))"
  [(parallel [(set (match_operand:SI 0 "gpr_operand" "")
		   (plus:SI (match_operand:SI 1 "gpr_operand" "")
			    (match_operand:SI 2 "nonmemory_operand" "")))
	      (clobber (reg:CC CC_REGNUM))])]
  "")

(define_peephole2
  [(parallel [(set (match_operand:SI 0 "gpr_operand" "")
		   (plus:SI (reg:SI GPR_SP)
			    (match_operand:SI 1 "nonmemory_operand" "")))
	      (clobber (reg:CC UNKNOWN_REGNUM))])]
  "(REG_P (operands[1]) && !reg_overlap_mentioned_p (operands[0], operands[1]))
   || RTX_OK_FOR_OFFSET_P (<MODE>mode, operands[1])"
  [(set (match_dup 0) (reg:SI GPR_SP))
   (set (mem:WMODE (post_modify (match_dup 0)
				(plus:SI (match_dup 0) (match_dup 1))))
	(reg:WMODE GPR_SP))]
  "")



(define_peephole2
  [(parallel [(set (match_operand:SI 0 "gpr_operand" "")
		   (plus:SI (reg:SI GPR_FP)
			    (match_operand:SI 1 "nonmemory_operand" "")))
	      (clobber (reg:CC UNKNOWN_REGNUM))])
   (match_scratch:WMODE 2 "r")]
  "frame_pointer_needed
   && ((REG_P (operands[1])
	&& !reg_overlap_mentioned_p (operands[0], operands[1]))
       || RTX_OK_FOR_OFFSET_P (<MODE>mode, operands[1]))"
  [(set (match_dup 0) (reg:SI GPR_FP))
   (set (match_dup 2)
	(mem:WMODE (post_modify (match_dup 0)
				(plus:SI (match_dup 0) (match_dup 1)))))]
  "")

(define_expand "subsi3"
  [(set (match_operand:SI 0 "gpr_operand" "")
	(plus:SI (match_operand:SI 1 "add_reg_operand" "")
		 (match_operand:SI 2 "arith_operand" "")))]
  ""
  "
{
  gcc_assert (!reload_in_progress && !reload_completed);

  if (TARGET_FP_IARITH)
    emit_insn (gen_isub (operands[0], operands[1], operands[2]));
  else
    emit_insn (gen_subsi3_i (operands[0], operands[1], operands[2]));
  DONE;
}")

(define_insn "subsi3_i"
  [(set (match_operand:SI 0 "gpr_operand" "=r")
	(minus:SI (match_operand:SI 1 "add_reg_operand" "r")
		  (match_operand:SI 2 "arith_operand" "rL")))
   (clobber (reg:CC CC_REGNUM))]
  ""
  "sub %0,%1,%2"
  [(set_attr "type" "misc")])

; After mode-switching, floating point operations, fp_sfuncs and calls
; must exhibit the use of the control register, lest the setting of the
; control register could be deleted or moved.  OTOH a use of a hard register
; greatly coundounds optimizers like the rtl loop optimizers or combine.
; Therefore, we put an extra pass immediately after the mode switching pass
; that inserts the USEs of the control registers, and sets a flag in struct
; machine_function that float_operation can henceforth only match with that
; USE.

;; Addition
(define_expand "addsf3"
  [(parallel
     [(set (match_operand:SF 0 "gpr_operand" "")
	   (plus:SF (match_operand:SF 1 "gpr_operand" "")
		    (match_operand:SF 2 "gpr_operand" "")))
      (clobber (reg:CC_FP CCFP_REGNUM))])])

(define_insn "*addsf3_i"
  [(match_parallel 3 "float_operation"
     [(set (match_operand:SF 0 "gpr_operand" "=r")
	   (plus:SF (match_operand:SF 1 "gpr_operand" "%r")
		    (match_operand:SF 2 "gpr_operand" "r")))
      (clobber (reg:CC_FP CCFP_REGNUM))])]
  ""
  "fadd %0,%1,%2"
  [(set_attr "type" "fp")])

;; Subtraction
(define_expand "subsf3"
  [(parallel
     [(set (match_operand:SF 0 "gpr_operand" "")
	   (minus:SF (match_operand:SF 1 "gpr_operand" "")
		     (match_operand:SF 2 "gpr_operand" "")))
      (clobber (reg:CC_FP CCFP_REGNUM))])])

(define_insn "*subsf3_i"
  [(match_parallel 3 "float_operation"
     [(set (match_operand:SF 0 "gpr_operand" "=r")
	   (minus:SF (match_operand:SF 1 "gpr_operand" "r")
		     (match_operand:SF 2 "gpr_operand" "r")))
      (clobber (reg:CC_FP CCFP_REGNUM))])]
  ""
  "fsub %0,%1,%2"
  [(set_attr "type" "fp")])

(define_expand "subsf3_f"
  [(parallel
     [(set (reg:CC_FP CCFP_REGNUM)
	   (compare:CC_FP (match_operand:SF 1 "gpr_operand" "r")
			  (match_operand:SF 2 "gpr_operand" "r")))
      (set (match_operand:SF 0 "gpr_operand" "=r")
	   (minus:SF (match_dup 1) (match_dup 2)))])]
  "!TARGET_SOFT_CMPSF")

(define_insn "*subsf3_f_i"
  [(match_parallel 3 "float_operation"
     [(set (reg:CC_FP CCFP_REGNUM)
	   (compare:CC_FP (match_operand:SF 1 "gpr_operand" "r")
			  (match_operand:SF 2 "gpr_operand" "r")))
      (set (match_operand:SF 0 "gpr_operand" "=r")
	   (minus:SF (match_dup 1) (match_dup 2)))])]
  "!TARGET_SOFT_CMPSF"
  "fsub %0,%1,%2"
  [(set_attr "type" "fp")])

; There is an fabs instruction, but it has longer latency.
(define_expand "abssf2"
  [(set (match_operand:SF 0 "gpr_operand" "")
	(abs:SF (match_operand:SF 1 "gpr_operand" "")))]
  ""
  "
{
  rtx op1 = copy_to_mode_reg (SImode, simplify_gen_subreg (SImode, operands[1],
							   SFmode, 0));
  rtx op0 = simplify_gen_subreg (SImode, operands[0], SFmode, 0);

  emit_insn (gen_ashlsi3 (op1, op1, const1_rtx));
  emit_insn (gen_lshrsi3 (op0, op1, const1_rtx));
  DONE;
}")

;; Multiplication
(define_expand "mulsf3"
  [(parallel
     [(set (match_operand:SF 0 "gpr_operand" "")
	   (mult:SF (match_operand:SF 1 "gpr_operand" "")
		    (match_operand:SF 2 "gpr_operand" "")))
      (clobber (reg:CC_FP CCFP_REGNUM))])])

(define_insn "*mulsf3_i"
  [(match_parallel 3 "float_operation"
     [(set (match_operand:SF 0 "gpr_operand" "=r")
	   (mult:SF (match_operand:SF 1 "gpr_operand" "%r")
		    (match_operand:SF 2 "gpr_operand" "r")))
      (clobber (reg:CC_FP CCFP_REGNUM))])]
  ""
  "fmul %0,%1,%2"
  [(set_attr "type" "fp")])

;; Division
(define_expand "divsf3"
  [(set (match_operand:SF 0 "gpr_operand" "")
	(div:SF (match_operand:SF 1 "gpr_operand" "")
		(match_operand:SF 2 "gpr_operand" "")))]
  "flag_reciprocal_math"
{
  rtx one = CONST1_RTX (SFmode);
  rtx dst = operands[0];

  if (rtx_equal_p (dst, operands[1]))
    {
      emit_move_insn (dst, one);
      DONE;
    }
  else if (!register_operand (dst, SFmode) && can_create_pseudo_p ())
    dst = gen_reg_rtx (SFmode);
  emit_insn (gen_recipsf2 (dst, one, operands[2],
			   sfunc_symbol (\"__fast_recipsf2\")));
  emit_insn (gen_mulsf3 (operands[0], operands[1], dst));
  DONE;
})

;; Before reload, keep the hard reg usage to clobbers so that the loop
;; optimizers can more easily move this insn.
;; It would be nicer to use a constraint for a GPR_0 - only register class,
;; but sched1 can still cause trouble then, and there is no guarantee of
;; better register allocations.
;; Neither is there when using the opposite strategy - putting explicit
;; hard register references into pre-reload rtl.
(define_expand "recipsf2"
  [(parallel
     [(set (match_operand:SF 0 "gpr_operand" "")
	   (div:SF (match_operand:SF 1 "const_float_1_operand" "")
		   (match_operand:SF 2 "move_src_operand" "")))
      (use (match_operand:SI 3 "move_src_operand" ""))
      (clobber (reg:SF 0))
      (clobber (reg:SI 1))
      (clobber (reg:SF GPR_IP))
      (clobber (reg:DI GPR_16))
      (clobber (reg:DI GPR_18))
      (clobber (reg:SI GPR_20))
      (clobber (reg:SI GPR_LR))
      (clobber (reg:CC CC_REGNUM))
      (clobber (reg:CC_FP CCFP_REGNUM))])])

(define_insn_and_split "*recipsf2_1"
  [(match_parallel 4 "float_operation"
     [(set (match_operand:SF 0 "gpr_operand" "=r,r")
	   (div:SF (match_operand:SF 1 "const_float_1_operand" "")
		   (match_operand:SF 2 "move_src_operand" "rU16m,rU16mCal")))
      (use (match_operand:SI 3 "move_src_operand" "rU16m,rU16mCal"))
      (clobber (reg:SF 0))
      (clobber (reg:SI 1))
      (clobber (reg:SF GPR_IP))
      (clobber (reg:DI GPR_16))
      (clobber (reg:DI GPR_18))
      (clobber (reg:SI GPR_20))
      (clobber (reg:SI GPR_LR))
      (clobber (reg:CC CC_REGNUM))
      (clobber (reg:CC_FP CCFP_REGNUM))])]
  "flag_reciprocal_math"
  "#"
  "&& reload_completed"
  [(set (reg:SI 1) (match_dup 3))
   (set (reg:SF 0) (match_dup 2))
   (parallel
     [(set (reg:SF 0)
	   (div:SF (match_dup 1)
		   (reg:SF 0)))
      (use (reg:SI 1))
      (clobber (reg:SI GPR_IP))
      (clobber (reg:DI GPR_16))
      (clobber (reg:DI GPR_18))
      (clobber (reg:SI GPR_20))
      (clobber (reg:SI GPR_LR))
      (clobber (reg:CC CC_REGNUM))
      (clobber (reg:CC_FP CCFP_REGNUM))
      (match_dup 5)
      (match_dup 6)])
   (set (match_dup 0) (reg:SF 0))]
  "operands[5] = XVECEXP (operands[4], 0, XVECLEN (operands[4], 0) - 2);
   operands[6] = XVECEXP (operands[4], 0, XVECLEN (operands[4], 0) - 1);"
  [(set_attr "type" "fp_sfunc")
   (set_attr "length" "16,24")])

(define_insn "*recipsf2_2"
  [(match_parallel 1 "float_operation"
     [(set (reg:SF 0)
	   (div:SF (match_operand:SF 0 "const_float_1_operand" "")
		   (reg:SF 0)))
      (use (reg:SI 1))
      (clobber (reg:SI GPR_IP))
      (clobber (reg:DI GPR_16))
      (clobber (reg:DI GPR_18))
      (clobber (reg:SI GPR_20))
      (clobber (reg:SI GPR_LR))
      (clobber (reg:CC CC_REGNUM))
      (clobber (reg:CC_FP CCFP_REGNUM))])]
  "flag_reciprocal_math"
  "jalr r1"
  [(set_attr "type" "fp_sfunc")])


;; Fused multiply-add
(define_expand "fmasf4"
  [(parallel
     [(set (match_operand:SF 0 "gpr_operand" "")
	   (fma:SF (match_operand:SF 1 "gpr_operand" "")
		   (match_operand:SF 2 "gpr_operand" "")
		   (match_operand:SF 3 "gpr_operand" "")))
      (clobber (reg:CC_FP CCFP_REGNUM))])]
  "")

; The multiply operands are commutative, but since they have the
; same constraints, there is no point in telling reload about this.
(define_insn "*fmadd"
  [(match_parallel 4 "float_operation"
     [(set (match_operand:SF 0 "gpr_operand" "=r")
	   (fma:SF (match_operand:SF 1 "gpr_operand" "r")
		   (match_operand:SF 2 "gpr_operand" "r")
		   (match_operand:SF 3 "gpr_operand" "0")))
      (clobber (reg:CC_FP CCFP_REGNUM))])]
  ""
  "fmadd %0,%1,%2"
  [(set_attr "type" "fp")])

; Once vetorization consistently works for this port, should check
; if the fmadd / fmsub patterns still serve a purpose.  With the
; introduction of fma / fnma handling by the SSA optimizers,
; at least scalars should be handled by these optimizers, would
; have to see how well they do on vectors from auto-vectorization.
;
; combiner pattern, also used by vector combiner pattern
(define_expand "maddsf"
  [(parallel
     [(set (match_operand:SF 0 "gpr_operand" "=r")
	   (plus:SF (mult:SF (match_operand:SF 1 "gpr_operand" "r")
			     (match_operand:SF 2 "gpr_operand" "r"))
		    (match_operand:SF 3 "gpr_operand" "0")))
      (clobber (reg:CC_FP CCFP_REGNUM))])]
  "TARGET_FUSED_MADD")

(define_insn "*maddsf_combine"
  [(match_parallel 4 "float_operation"
     [(set (match_operand:SF 0 "gpr_operand" "=r")
	   (plus:SF (mult:SF (match_operand:SF 1 "gpr_operand" "r")
			     (match_operand:SF 2 "gpr_operand" "r"))
		    (match_operand:SF 3 "gpr_operand" "0")))
      (clobber (reg:CC_FP CCFP_REGNUM))])]
  "TARGET_FUSED_MADD"
  "fmadd %0,%1,%2"
  [(set_attr "type" "fp")])

;; Fused multiply-sub
(define_expand "fnmasf4"
  [(parallel
     [(set (match_operand:SF 0 "gpr_operand" "")
	   (fma:SF (neg:SF (match_operand:SF 1 "gpr_operand" ""))
		   (match_operand:SF 2 "gpr_operand" "")
		   (match_operand:SF 3 "gpr_operand" "")))
      (clobber (reg:CC_FP CCFP_REGNUM))])]
  "")

(define_insn "*fmsub"
  [(match_parallel 4 "float_operation"
     [(set (match_operand:SF 0 "gpr_operand" "=r")
	   (fma:SF (neg:SF (match_operand:SF 1 "gpr_operand" "r"))
		   (match_operand:SF 2 "gpr_operand" "r")
		   (match_operand:SF 3 "gpr_operand" "0")))
      (clobber (reg:CC_FP CCFP_REGNUM))])]
  ""
  "fmsub %0,%1,%2"
  [(set_attr "type" "fp")])

(define_insn "*fmsub_combine"
  [(match_parallel 4 "float_operation"
     [(set (match_operand:SF 0 "gpr_operand" "=r")
	   (minus:SF  (match_operand:SF 3 "gpr_operand" "0")
		      (mult:SF (match_operand:SF 1 "gpr_operand" "r")
			       (match_operand:SF 2 "gpr_operand" "r"))))
      (clobber (reg:CC_FP CCFP_REGNUM))])]
  "TARGET_FUSED_MADD"
  "fmsub %0,%1,%2"
  [(set_attr "type" "fp")])

;; float / integer conversions

(define_expand "floatsisf2"
  [(parallel
     [(set (match_operand:SF 0 "gpr_operand" "")
	   (float:SF (match_operand:SI 1 "gpr_operand" "")))
      (clobber (reg:CC_FP CCFP_REGNUM))])])

(define_insn "*floatsisf2_i"
  [(match_parallel 2 "float_operation"
     [(set (match_operand:SF 0 "gpr_operand" "=r")
	   (float:SF (match_operand:SI 1 "gpr_operand" "r")))
      (clobber (reg:CC_FP CCFP_REGNUM))])]
  ""
  "float %0, %1"
  [(set_attr "type" "fp")])

(define_expand "floatsisf2_cmp"
  [(parallel
     [(set (reg:CC_FP CCFP_REGNUM)
	   (compare:CC_FP (float:SF (match_operand:SF 1 "gpr_operand" "r"))
			  (match_dup 2)))
      (set (match_operand:SF 0 "gpr_operand" "=r")
	   (float:SF (match_dup 1)))])]
  ""
  "operands[2] = CONST0_RTX (SFmode);")

(define_insn "*floatsisf2_cmp_i"
  [(match_parallel 3 "float_operation"
     [(set (reg:CC_FP CCFP_REGNUM)
	   (compare:CC_FP (float:SF (match_operand:SF 1 "gpr_operand" "r"))
			  (match_operand:SF 2 "const0_operand" "")))
      (set (match_operand:SF 0 "gpr_operand" "=r")
	   (float:SF (match_dup 1)))])]
  ""
  "float %0, %1"
  [(set_attr "type" "fp")])

(define_expand "floatunssisf2"
  [(set (match_operand:SF 0 "gpr_operand" "")
	(float:SF (match_operand:SI 1 "gpr_operand" "")))]
  "epiphany_normal_fp_rounding == /*FP_MODE_ROUND_TRUNC*/ 2"
{
  rtx cst = force_reg (SImode, gen_int_mode (0xb0800000, SImode));
  rtx tmp = gen_reg_rtx (SImode);
  rtx cmp = gen_rtx_GTU (VOIDmode, gen_rtx_REG (CCmode, CC_REGNUM), const0_rtx);

  if (reg_overlap_mentioned_p (operands[0], operands[1]))
    operands[1] = copy_to_mode_reg (SImode, operands[1]);
  emit_insn (gen_floatsisf2 (operands[0], operands[1]));
  emit_insn (gen_ashrsi3 (tmp, operands[1], GEN_INT (8)));
  emit_insn (gen_sub_f (tmp, tmp, cst));
  emit_insn (gen_movsfcc (operands[0], cmp,
			  simplify_gen_subreg (SFmode, tmp, SImode, 0),
			  operands[0]));
  DONE;
})

(define_expand "fix_truncsfsi2"
  [(parallel
     [(set (match_operand:SI 0 "gpr_operand" "")
	   (fix:SI (match_operand:SF 1 "gpr_operand" "")))
      (clobber (reg:CC_FP CCFP_REGNUM))])])

(define_insn "*fix_truncsfsi2_i"
  [(match_parallel 2 "float_operation"
     [(set (match_operand:SI 0 "gpr_operand" "=r")
	   (fix:SI (match_operand:SF 1 "gpr_operand" "r")))
      (clobber (reg:CC_FP CCFP_REGNUM))])]
  ""
  "fix %0, %1"
  [(set_attr "type" "fp")
   (set (attr "fp_mode")
	(cond [(match_test "TARGET_MAY_ROUND_FOR_TRUNC")
	       (const_string "round_unknown")]
	      (const_string "round_trunc")))])

(define_expand "fixuns_truncsfsi2"
  [(set (match_operand:SI 0 "gpr_operand" "")
	(unsigned_fix:SI (match_operand:SF 1 "gpr_operand" "")))]
  ""
{
  if (reg_overlap_mentioned_p (operands[0], operands[1]))
    operands[1] = copy_to_mode_reg (SImode, operands[1]);
  if (TARGET_SOFT_CMPSF || optimize_function_for_speed_p (cfun))
    {
      rtx op1si;
      /* By toggling what it to be bit31 before the shift, we get a chance to
	 use a short movt insn.  */
      rtx bit31 = force_reg (SImode, GEN_INT (0x800000));
      rtx tmp = gen_reg_rtx (SImode);
      rtx limit = force_reg (SImode, gen_int_mode (0x4f000000, SImode));
      rtx cmp
	= gen_rtx_GE (VOIDmode, gen_rtx_REG (CCmode, CC_REGNUM), const0_rtx);

      op1si = simplify_gen_subreg (SImode, operands[1], SFmode, 0);
      emit_insn (gen_fix_truncsfsi2 (operands[0], operands[1]));
      emit_insn (gen_subsi3_i (tmp, op1si, bit31));
      emit_insn (gen_ashlsi3 (tmp, tmp, GEN_INT (8)));
      emit_insn (gen_cmpsi_cc_insn (op1si, limit));
      emit_insn (gen_movsicc (operands[0], cmp, tmp, operands[0]));
    }
  else
    {
      REAL_VALUE_TYPE offset;
      rtx limit;
      rtx tmp = gen_reg_rtx (SFmode);
      rtx label = gen_label_rtx ();
      rtx bit31;
      rtx cc1 = gen_rtx_REG (CC_FPmode, CCFP_REGNUM);
      rtx cmp = gen_rtx_LT (VOIDmode, cc1, CONST0_RTX (SFmode));

      real_2expN (&offset, 31, SFmode);
      limit = CONST_DOUBLE_FROM_REAL_VALUE (offset, SFmode);
      limit = force_reg (SFmode, limit);
      emit_insn (gen_fix_truncsfsi2 (operands[0], operands[1]));
      emit_insn (gen_subsf3_f (tmp, operands[1], limit));
      emit_jump_insn (gen_branch_insn (label, cmp, cc1));
      bit31 = force_reg (SImode, gen_int_mode (0x80000000, SImode));
      emit_insn (gen_fix_truncsfsi2 (operands[0], tmp));
      emit_insn (gen_xorsi3 (operands[0], operands[0], bit31));
      emit_label (label);
    }
  DONE;
})

(define_expand "iadd"
  [(parallel
     [(set (match_operand:SF 0 "gpr_operand" "")
	   (plus:SI (match_operand:SF 1 "gpr_operand" "")
		    (match_operand:SF 2 "gpr_operand" "")))
      (clobber (reg:CC_FP CCFP_REGNUM))])])

(define_insn "*iadd_i"
  [(match_parallel 3 "float_operation"
     [(set (match_operand:SI 0 "gpr_operand" "=r")
	   (plus:SI (match_operand:SI 1 "gpr_operand" "%r")
		    (match_operand:SI 2 "gpr_operand" "r")))
      (clobber (reg:CC_FP CCFP_REGNUM))])]
  ""
  "iadd %0, %1, %2"
  [(set_attr "type" "fp_int")])

(define_expand "isub"
  [(parallel
     [(set (match_operand:SF 0 "gpr_operand" "")
	   (minus:SI (match_operand:SF 1 "gpr_operand" "")
		     (match_operand:SF 2 "gpr_operand" "")))
      (clobber (reg:CC_FP CCFP_REGNUM))])])

(define_insn "*isub_i"
  [(match_parallel 3 "float_operation"
     [(set (match_operand:SI 0 "gpr_operand" "=r")
	   (minus:SI (match_operand:SI 1 "gpr_operand" "r")
		     (match_operand:SI 2 "gpr_operand" "r")))
      (clobber (reg:CC_FP CCFP_REGNUM))])]
  ""
  "isub %0, %1, %2"
  [(set_attr "type" "fp_int")])

; Try to figure out if we over-committed the FPU, and if so, move
; some insns back over to the integer pipe.

; The peephole optimizer 'consumes' the insns that are explicitly
; mentioned.  We do not want the preceding insn reconsidered, but
; we do want that for the following one, so that if we have a run
; of five fpu users, two of them get changed.  Therefore, we
; use next_active_insn to look at the 'following' insn.  That should
; exist, because peephole2 runs after reload, and there has to be
; a return after an fp_int insn.
; ??? However, we can not even ordinarily match the preceding insn;
; there is some bug in the generators such that then it leaves out
; the check for PARALLEL before the length check for the then-second
; main insn.  Observed when compiling compatibility-atomic-c++0x.cc
; from libstdc++-v3.
(define_peephole2
  [(match_parallel 3 "float_operation"
     [(set (match_operand:SI 0 "gpr_operand" "")
	   (match_operator:SI 4 "addsub_operator"
	     [(match_operand:SI 1 "gpr_operand" "")
	      (match_operand:SI 2 "gpr_operand" "")]))
      (clobber (reg:CC_FP CCFP_REGNUM))])]
  "get_attr_sched_use_fpu (prev_active_insn (peep2_next_insn (0)))
   && peep2_regno_dead_p (1, CC_REGNUM)
   && get_attr_sched_use_fpu (next_active_insn (peep2_next_insn (0)))"
  [(parallel [(set (match_dup 0) (match_dup 4))
	      (clobber (reg:CC CC_REGNUM))])]
)

(define_expand "mulsi3"
  [(parallel
     [(set (match_operand:SI 0 "gpr_operand" "")
	   (mult:SI (match_operand:SI 1 "gpr_operand" "")
		    (match_operand:SI 2 "gpr_operand" "")))
      (clobber (reg:CC_FP CCFP_REGNUM))])])

(define_insn "*imul"
  [(match_parallel 3 "float_operation"
     [(set (match_operand:SI 0 "gpr_operand" "=r")
	   (mult:SI (match_operand:SI 1 "gpr_operand" "%r")
		    (match_operand:SI 2 "gpr_operand" "r")))
      (clobber (reg:CC_FP CCFP_REGNUM))])]
  ""
  "imul %0, %1, %2"
  [(set_attr "type" "fp_int")])

; combiner pattern, also used by vector combiner pattern
(define_expand "maddsi"
  [(parallel
     [(set (match_operand:SI 0 "gpr_operand" "=r")
	   (plus:SI (mult:SI (match_operand:SI 1 "gpr_operand" "r")
			     (match_operand:SI 2 "gpr_operand" "r"))
		    (match_operand:SI 3 "gpr_operand" "0")))
      (clobber (reg:CC_FP CCFP_REGNUM))])]
  "")

(define_insn "*maddsi_combine"
  [(match_parallel 4 "float_operation"
     [(set (match_operand:SI 0 "gpr_operand" "=r")
	   (plus:SI (mult:SI (match_operand:SI 1 "gpr_operand" "r")
			     (match_operand:SI 2 "gpr_operand" "r"))
		    (match_operand:SI 3 "gpr_operand" "0")))
      (clobber (reg:CC_FP CCFP_REGNUM))])]
  ""
  "imadd %0, %1, %2"
  [(set_attr "type" "fp_int")])

(define_insn "*imsub"
  [(match_parallel 4 "float_operation"
     [(set (match_operand:SI 0 "gpr_operand" "=r")
	   (minus:SI (match_operand:SI 3 "gpr_operand" "0")
		     (mult:SI (match_operand:SI 1 "gpr_operand" "r")
			      (match_operand:SI 2 "gpr_operand" "r"))))
      (clobber (reg:CC_FP CCFP_REGNUM))])]
  ""
  "imsub %0, %1, %2"
  [(set_attr "type" "fp_int")])

(define_expand "divsi3"
  [(parallel
     [(set (match_operand:SI 0 "move_dest_operand" "")
	   (div:SI (match_operand:SI 1 "move_src_operand" "")
		   (match_operand:SI 2 "move_src_operand" "")))
      (use (match_dup 3))
      (clobber (reg:SI 0))
      (clobber (reg:SI 1))
      (clobber (reg:SI GPR_IP))
      (clobber (reg:DI GPR_16))
      (clobber (reg:DI GPR_18))
      (clobber (reg:SI GPR_20))
      (clobber (reg:SI GPR_LR))
      (clobber (reg:CC CC_REGNUM))
      (clobber (reg:CC_FP CCFP_REGNUM))])]
  ""
  "operands[3] = sfunc_symbol (\"__divsi3\");")

;; Before reload, keep the hard reg usage to clobbers so that the loop
;; optimizers can more easily move this insn.
(define_insn_and_split "*divsi3_1"
  [(match_parallel 4 "float_operation"
     [(set (match_operand:SI 0 "move_dest_operand" "=r,r")
	   (div:SI (match_operand:SI 1 "move_src_operand" "rU16m,rU16mCal")
		   (match_operand:SI 2 "move_src_operand" "rU16m,rU16mCal")))
      (use (match_operand:SI 3 "call_address_operand" "Csy,r"))
      (clobber (reg:SI 0))
      (clobber (reg:SI 1))
      (clobber (reg:SI GPR_IP))
      (clobber (reg:DI GPR_16))
      (clobber (reg:DI GPR_18))
      (clobber (reg:SI GPR_20))
      (clobber (reg:SI GPR_LR))
      (clobber (reg:CC CC_REGNUM))
      (clobber (reg:CC_FP CCFP_REGNUM))])]
  ""
  "#"
  "&& reload_completed"
  [(set (reg:SI 0) (match_dup 1))
   (set (reg:SI 1) (match_dup 2))
   (parallel
     [(set (reg:SI 0) (div:SI (reg:SI 0) (reg:SI 1)))
      (use (match_dup 3))
      (clobber (reg:SI 1))
      (clobber (reg:SI GPR_IP))
      (clobber (reg:DI GPR_16))
      (clobber (reg:DI GPR_18))
      (clobber (reg:SI GPR_20))
      (clobber (reg:SI GPR_LR))
      (clobber (reg:CC CC_REGNUM))
      (clobber (reg:CC_FP CCFP_REGNUM))
      (match_dup 5)
      (match_dup 6)])
   (set (match_dup 0) (reg:SI 0))]
  "operands[5] = XVECEXP (operands[4], 0, XVECLEN (operands[4], 0) - 2);
   operands[6] = XVECEXP (operands[4], 0, XVECLEN (operands[4], 0) - 1);"
  [(set_attr "type" "fp_sfunc")
   (set_attr "length" "16,24")])

(define_insn "*divsi3_2"
  [(match_parallel 1 "float_operation"
     [(set (reg:SI 0) (div:SI (reg:SI 0) (reg:SI 1)))
      (use (match_operand:SI 0 "call_address_operand" "Csy,r"))
      (clobber (reg:SI 1))
      (clobber (reg:SI GPR_IP))
      (clobber (reg:DI GPR_16))
      (clobber (reg:DI GPR_18))
      (clobber (reg:SI GPR_20))
      (clobber (reg:SI GPR_LR))
      (clobber (reg:CC CC_REGNUM))
      (clobber (reg:CC_FP CCFP_REGNUM))])]
  ""
  "%f0"
  [(set_attr "type" "fp_sfunc")])

(define_expand "udivsi3"
  [(parallel
     [(set (match_operand:SI 0 "move_dest_operand" "")
	   (udiv:SI (match_operand:SI 1 "move_src_operand" "")
		    (match_operand:SI 2 "move_src_operand" "")))
      (use (match_dup 3))
      (clobber (reg:SI 0))
      (clobber (reg:SI 1))
      (clobber (reg:SI GPR_IP))
      (clobber (reg:DI GPR_16))
      (clobber (reg:SI GPR_18))
      (clobber (reg:SI GPR_LR))
      (clobber (reg:CC CC_REGNUM))
      (clobber (reg:CC_FP CCFP_REGNUM))])]
  ""
  "operands[3] = sfunc_symbol (\"__udivsi3\");")

;; Before reload, keep the hard reg usage to clobbers so that the loop
;; optimizers can more easily move this insn.
(define_insn_and_split "*udivsi3_1"
  [(match_parallel 4 "float_operation"
     [(set (match_operand:SI 0 "move_dest_operand" "=r,r")
	   (udiv:SI (match_operand:SI 1 "move_src_operand" "rU16m,rU16mCal")
		    (match_operand:SI 2 "move_src_operand" "rU16m,rU16mCal")))
      (use (match_operand:SI 3 "call_address_operand" "Csy,r"))
      (clobber (reg:SI 0))
      (clobber (reg:SI 1))
      (clobber (reg:SI GPR_IP))
      (clobber (reg:DI GPR_16))
      (clobber (reg:SI GPR_18))
      (clobber (reg:SI GPR_LR))
      (clobber (reg:CC CC_REGNUM))
      (clobber (reg:CC_FP CCFP_REGNUM))])]
  ""
  "#"
  "&& reload_completed"
  [(set (reg:SI 0) (match_dup 1))
   (set (reg:SI 1) (match_dup 2))
   (parallel
     [(set (reg:SI 0) (udiv:SI (reg:SI 0) (reg:SI 1)))
      (use (match_dup 3))
      (clobber (reg:SI 1))
      (clobber (reg:SI GPR_IP))
      (clobber (reg:DI GPR_16))
      (clobber (reg:SI GPR_18))
      (clobber (reg:SI GPR_LR))
      (clobber (reg:CC CC_REGNUM))
      (clobber (reg:CC_FP CCFP_REGNUM))
      (match_dup 5)
      (match_dup 6)])
   (set (match_dup 0) (reg:SI 0))]
  "operands[5] = XVECEXP (operands[4], 0, XVECLEN (operands[4], 0) - 2);
   operands[6] = XVECEXP (operands[4], 0, XVECLEN (operands[4], 0) - 1);"
  [(set_attr "type" "fp_sfunc")
   (set_attr "length" "16,24")])

(define_insn "*udivsi3_2"
  [(match_parallel 1 "float_operation"
     [(set (reg:SI 0) (udiv:SI (reg:SI 0) (reg:SI 1)))
      (use (match_operand:SI 0 "call_address_operand" "Csy,r"))
      (clobber (reg:SI 1))
      (clobber (reg:SI GPR_IP))
      (clobber (reg:DI GPR_16))
      (clobber (reg:SI GPR_18))
      (clobber (reg:SI GPR_LR))
      (clobber (reg:CC CC_REGNUM))
      (clobber (reg:CC_FP CCFP_REGNUM))])]
  ""
  "%f0"
  [(set_attr "type" "fp_sfunc")])

(define_expand "modsi3"
  [(parallel
     [(set (match_operand:SI 0 "move_dest_operand" "")
	   (mod:SI (match_operand:SI 1 "move_src_operand" "")
		   (match_operand:SI 2 "move_src_operand" "")))
      (use (match_dup 3))
      (clobber (reg:SI 0))
      (clobber (reg:SI 1))
      (clobber (reg:SI 2))
      (clobber (reg:SI GPR_IP))
      (clobber (reg:DI GPR_16))
      (clobber (reg:DI GPR_18))
      (clobber (reg:SI GPR_LR))
      (clobber (reg:CC CC_REGNUM))
      (clobber (reg:CC_FP CCFP_REGNUM))])]
  ""
  "operands[3] = sfunc_symbol (\"__modsi3\");")

;; Before reload, keep the hard reg usage to clobbers so that the loop
;; optimizers can more easily move this insn.
(define_insn_and_split "*modsi3_1"
  [(match_parallel 4 "float_operation"
     [(set (match_operand:SI 0 "move_dest_operand" "=r,r")
	   (mod:SI (match_operand:SI 1 "move_src_operand" "rU16m,rU16mCal")
		   (match_operand:SI 2 "move_src_operand" "rU16m,rU16mCal")))
      (use (match_operand:SI 3 "call_address_operand" "Csy,r"))
      (clobber (reg:SI 0))
      (clobber (reg:SI 1))
      (clobber (reg:SI 2))
      (clobber (reg:SI GPR_IP))
      (clobber (reg:DI GPR_16))
      (clobber (reg:DI GPR_18))
      (clobber (reg:SI GPR_LR))
      (clobber (reg:CC CC_REGNUM))
      (clobber (reg:CC_FP CCFP_REGNUM))])]
  ""
  "#"
  "&& reload_completed"
  [(set (reg:SI 0) (match_dup 1))
   (set (reg:SI 1) (match_dup 2))
   (parallel
     [(set (reg:SI 0) (mod:SI (reg:SI 0) (reg:SI 1)))
      (use (match_dup 3))
      (clobber (reg:SI 2))
      (clobber (reg:SI GPR_IP))
      (clobber (reg:DI GPR_16))
      (clobber (reg:DI GPR_18))
      (clobber (reg:SI GPR_LR))
      (clobber (reg:CC CC_REGNUM))
      (clobber (reg:CC_FP CCFP_REGNUM))
      (match_dup 5)
      (match_dup 6)])
   (set (match_dup 0) (reg:SI 0))]
  "operands[5] = XVECEXP (operands[4], 0, XVECLEN (operands[4], 0) - 2);
   operands[6] = XVECEXP (operands[4], 0, XVECLEN (operands[4], 0) - 1);"
  [(set_attr "type" "fp_sfunc")
   (set_attr "length" "16,24")])

(define_insn "*modsi3_2"
  [(match_parallel 1 "float_operation"
     [(set (reg:SI 0) (mod:SI (reg:SI 0) (reg:SI 1)))
      (use (match_operand:SI 0 "call_address_operand" "Csy,r"))
      (clobber (reg:SI 2))
      (clobber (reg:SI GPR_IP))
      (clobber (reg:DI GPR_16))
      (clobber (reg:DI GPR_18))
      (clobber (reg:SI GPR_LR))
      (clobber (reg:CC CC_REGNUM))
      (clobber (reg:CC_FP CCFP_REGNUM))])]
  ""
  "%f0"
  [(set_attr "type" "fp_sfunc")])

(define_expand "umodsi3"
  [(parallel
     [(set (match_operand:SI 0 "move_dest_operand" "")
	   (umod:SI (match_operand:SI 1 "move_src_operand" "")
		    (match_operand:SI 2 "move_src_operand" "")))
      (use (match_dup 3))
      (clobber (reg:SI 0))
      (clobber (reg:SI 1))
      (clobber (reg:SI 2))
      (clobber (reg:SI GPR_IP))
      (clobber (reg:DI GPR_16))
      (clobber (reg:SI GPR_LR))
      (clobber (reg:CC CC_REGNUM))
      (clobber (reg:CC_FP CCFP_REGNUM))])]
  ""
  "operands[3] = sfunc_symbol (\"__umodsi3\");")

;; Before reload, keep the hard reg usage to clobbers so that the loop
;; optimizers can more easily move this insn.
(define_insn_and_split "*umodsi3_1"
  [(match_parallel 4 "float_operation"
     [(set (match_operand:SI 0 "move_dest_operand" "=r,r")
	   (umod:SI (match_operand:SI 1 "move_src_operand" "rU16m,rU16mCal")
		    (match_operand:SI 2 "move_src_operand" "rU16m,rU16mCal")))
      (use (match_operand:SI 3 "call_address_operand" "Csy,r"))
      (clobber (reg:SI 0))
      (clobber (reg:SI 1))
      (clobber (reg:SI 2))
      (clobber (reg:SI GPR_IP))
      (clobber (reg:DI GPR_16))
      (clobber (reg:SI GPR_LR))
      (clobber (reg:CC CC_REGNUM))
      (clobber (reg:CC_FP CCFP_REGNUM))])]
  ""
  "#"
  "&& reload_completed"
  [(set (reg:SI 0) (match_dup 1))
   (set (reg:SI 1) (match_dup 2))
   (parallel
     [(set (reg:SI 0) (umod:SI (reg:SI 0) (reg:SI 1)))
      (use (match_dup 3))
      (clobber (reg:SI 2))
      (clobber (reg:SI GPR_IP))
      (clobber (reg:DI GPR_16))
      (clobber (reg:SI GPR_LR))
      (clobber (reg:CC CC_REGNUM))
      (clobber (reg:CC_FP CCFP_REGNUM))
      (match_dup 5)
      (match_dup 6)])
   (set (match_dup 0) (reg:SI 0))]
  "operands[5] = XVECEXP (operands[4], 0, XVECLEN (operands[4], 0) - 2);
   operands[6] = XVECEXP (operands[4], 0, XVECLEN (operands[4], 0) - 1);"
  [(set_attr "type" "fp_sfunc")
   (set_attr "length" "16,24")])

(define_insn "*umodsi3_2"
  [(match_parallel 1 "float_operation"
     [(set (reg:SI 0) (umod:SI (reg:SI 0) (reg:SI 1)))
      (use (match_operand:SI 0 "call_address_operand" "Csy,r"))
      (clobber (reg:SI 2))
      (clobber (reg:SI GPR_IP))
      (clobber (reg:DI GPR_16))
      (clobber (reg:SI GPR_LR))
      (clobber (reg:CC CC_REGNUM))
      (clobber (reg:CC_FP CCFP_REGNUM))])]
  ""
  "%f0"
  [(set_attr "type" "fp_sfunc")])

; Disable interrupts.
; Any earlier values read from CONFIG_REGNUM are out of date, since interrupts
; might have changed settings that we do not want to mess with.
(define_insn "gid"
  [(set (reg:SI CONFIG_REGNUM)
	(unspec_volatile:SI [(const_int 0)] UNSPECV_GID))]
  ""
  "gid"
  [(set_attr "type" "flow")])

; Enable interrupts.
; Present CONTROL_REGNUM here to make sure it is live before the
; actual uses in floating point insns / calls are inserted.
; FWIW, interrupts also do mind what is in the control register.
(define_insn "gie"
  [(unspec_volatile [(reg:SI CONFIG_REGNUM)] UNSPECV_GIE)]
  ""
  "gie"
  [(set_attr "type" "flow")])

; Floating point instructions require manipulating the control register.
; Manipulating the control register needs aritmetic.
; Arithmetic clobbers flags.
; The flags are in the status register, which also contains the alternate
; flag and the interrupt enable/disable bits.
; saving/restoring status and mixing up the order with gid/gie could
; lead to disaster.
; Usually, saving/restoring the status is unnecessary, and will be optimized
; away.  But when we really need it, we must make sure that we don't change
; anything but the flags.
; N.B.: We could make the constant easier to load by inverting it, but
; then we'd need to clobber the saved value - and that would make optimizing
; away unneeded saves/restores harder / less likely.
(define_expand "movcc"
  [(parallel [(set (match_operand:CC 0 "cc_move_operand"  "")
		   (match_operand:CC 1 "cc_move_operand" ""))
	      (use (match_dup 2))
	      (clobber (match_scratch:SI 3                 "=X, &r"))])]
  ""
  "operands[2] = gen_int_mode (~0x10f0, SImode);")

(define_insn "*movcc_i"
  [(set (match_operand:CC 0 "cc_move_operand"  "=r,Rcc")
	(match_operand:CC 1 "cc_move_operand" "Rcc,  r"))
   (use (match_operand:SI 2 "nonmemory_operand"  "X,  r"))
   (clobber (match_scratch:SI 3                 "=X, &r"))]
  ""
  "@
   movfs %0,status
   movfs %3,status\;eor %3,%3,%1\;and %3,%3,%2\;eor %3,%3,%1\;movts status,%3"
  [(set_attr "type" "flow")
   (set_attr "length" "20,4")])

(define_insn_and_split "save_config"
  [(set (match_operand:SI 0 "gpr_operand" "=r") (reg:SI CONFIG_REGNUM))
   (use (reg:SI FP_NEAREST_REGNUM))
   (use (reg:SI FP_TRUNCATE_REGNUM))
   (use (reg:SI FP_ANYFP_REGNUM))]
  ""
  "#"
  "reload_completed"
  [(set (match_dup 0) (reg:SI CONFIG_REGNUM))])

(define_insn_and_split "set_fp_mode"
  [(set (reg:SI FP_NEAREST_REGNUM)
	(match_operand:SI 0 "set_fp_mode_operand" "rCfm"))
   (set (reg:SI FP_TRUNCATE_REGNUM) (match_dup 0))
   (set (reg:SI FP_ANYFP_REGNUM)
	(match_operand:SI 1 "set_fp_mode_operand" "rCfm"))
   (use (match_operand:SI 2 "gpr_operand" "r"))
   (clobber (reg:CC CC_REGNUM))
   (clobber (match_scratch:SI 3 "=&r"))]
  ""
  "#"
  "reload_completed || !rtx_equal_p (operands[0], operands[1])"
  [(const_int 0)]
{
  if (!reload_completed)
    emit_note (NOTE_INSN_DELETED);
  else
    epiphany_expand_set_fp_mode (operands);
  DONE;
})


;; Boolean instructions.
;;
;; We don't define the DImode versions as expand_binop does a good enough job.

(define_insn "andsi3"
  [(set (match_operand:SI 0 "gpr_operand" "=r")
	(and:SI (match_operand:SI 1 "gpr_operand" "r")
		(match_operand:SI 2 "gpr_operand" "r")))
   (clobber (reg:CC CC_REGNUM))]
  ""
  "and %0,%1,%2")

(define_insn "iorsi3"
  [(set (match_operand:SI 0 "gpr_operand" "=r")
	(ior:SI (match_operand:SI 1 "gpr_operand" "r")
		(match_operand:SI 2 "gpr_operand" "r")))
   (clobber (reg:CC CC_REGNUM))]
  ""
  "orr %0,%1,%2")

(define_insn "xorsi3"
  [(set (match_operand:SI 0 "gpr_operand" "=r")
	(xor:SI (match_operand:SI 1 "gpr_operand" "r")
		(match_operand:SI 2 "gpr_operand" "r")))
   (clobber (reg:CC CC_REGNUM))]
  ""
  "eor %0,%1,%2")

(define_expand "one_cmplsi2"
  [(set (match_operand:SI 0 "gpr_operand" "")
	(xor:SI (match_operand:SI 1 "gpr_operand" "")
		(match_dup 2)))]
  ""
{
  if (epiphany_m1reg >= 0)
    emit_insn (gen_one_cmplsi2_i (operands[0], operands[1]));
  else
    emit_insn (gen_xorsi3 (operands[0], operands[1],
			   force_reg (SImode, GEN_INT (-1))));
  DONE;
})

; Note that folding this pattern into the xorsi3 pattern would make combine
; less effective.
(define_insn "one_cmplsi2_i"
  [(set (match_operand:SI 0 "gpr_operand" "=r")
        (not:SI (match_operand:SI 1 "gpr_operand" "r")))
   (clobber (reg:CC CC_REGNUM))]
  "epiphany_m1reg >= 0"
  "eor %0,%1,%-")

;; Shift instructions.
;; In principle we could support arbitrary symbolic values as shift constant
;; (truncating the value appropriately), but that would require a suitable
;; relocation and assembler & linker support.
(define_insn "ashrsi3"
  [(set (match_operand:SI 0 "gpr_operand" "=r,r")
	(ashiftrt:SI (match_operand:SI 1 "gpr_operand" "r,r")
		     (match_operand:SI 2 "arith_operand" "r,K")))
   (clobber (reg:CC CC_REGNUM))]
  ""
  "asr %0,%1,%2"
  [(set_attr "length" "4")
   (set_attr "type" "shift")])

(define_insn "ashrsi3_tst"
  [(set (reg:CC CC_REGNUM)
	(compare:CC
	  (ashiftrt:SI (match_operand:SI 1 "gpr_operand" "r,r")
		       (match_operand:SI 2 "arith_operand" "r,K"))
	(const_int 0)))
   (set (match_operand:SI 0 "gpr_operand" "=r,r")
	(ashiftrt:SI (match_dup 1) (match_dup 2)))]
  ""
  "asr %0,%1,%2"
  [(set_attr "length" "4")
   (set_attr "type" "shift")])

;; Logical Shift Right
(define_insn "lshrsi3"
  [(set (match_operand:SI 0 "gpr_operand" "=r,r")
	(lshiftrt:SI (match_operand:SI 1 "gpr_operand" "r,r")
		     (match_operand:SI 2 "arith_operand" "r,K")))
   (clobber (reg:CC CC_REGNUM))]
  ""
  "lsr %0,%1,%2"
  [(set_attr "length" "4")
   (set_attr "type" "shift")])

(define_insn "lshrsi3_tst"
  [(set (reg:CC CC_REGNUM)
	(compare:CC
	  (lshiftrt:SI (match_operand:SI 1 "gpr_operand" "r,r")
		       (match_operand:SI 2 "arith_operand" "r,K"))
	(const_int 0)))
   (set (match_operand:SI 0 "gpr_operand" "=r,r")
	(lshiftrt:SI (match_dup 1) (match_dup 2)))]
  ""
  "lsr %0,%1,%2"
  [(set_attr "length" "4")
   (set_attr "type" "shift")])

;; Logical/Arithmetic Shift Left
(define_insn "ashlsi3"
  [(set (match_operand:SI 0 "gpr_operand" "=r,r")
	(ashift:SI (match_operand:SI 1 "gpr_operand" "r,r")
		   (match_operand:SI 2 "arith_operand" "r,K")))
   (clobber (reg:CC CC_REGNUM))]
  ""
  "lsl %0,%1,%2"
  [(set_attr "length" "4")
   (set_attr "type" "shift")])

(define_insn "*ashlsi_btst"
  [(set (reg:CC_N_NE CC_REGNUM)
	(compare:CC_N_NE
	  (zero_extract:SI (match_operand:SI 1 "gpr_operand" "r")
			   (const_int 1)
			   (match_operand 2 "const_int_operand" "K"))
	  (const_int 0)))
   (clobber (match_scratch:SI 0 "=r"))]
  ""
{
  rtx xop[3];

  xop[0] = operands[0];
  xop[1] = operands[1];
  xop[2] = GEN_INT (31-INTVAL (operands[2]));
  output_asm_insn ("lsl %0,%1,%2", xop);
  return "";
})

;; zero extensions
(define_insn_and_split "zero_extendqisi2"
  [(set (match_operand:SI 0 "register_operand" "=r,r")
	(zero_extend:SI (match_operand:QI 1 "nonimmediate_operand" "r,m")))
   (clobber (reg:CC CC_REGNUM))]
  ""
  "@
   #
   ldrb %0,%1"
  "reload_completed
   ? true_regnum (operands[1]) >= 0
   : REG_P (operands[1]) && REGNO (operands[1]) < FIRST_PSEUDO_REGISTER"
  [(parallel [(set (match_dup 0) (ashift:SI (match_dup 2) (const_int 24)))
	      (clobber (reg:CC CC_REGNUM))])
   (parallel [(set (match_dup 0) (lshiftrt:SI (match_dup 0) (const_int 24)))
	      (clobber (reg:CC CC_REGNUM))])]
  "operands[2] = simplify_gen_subreg (SImode, operands[1], QImode, 0);")

(define_insn "zero_extendhisi2"
  [(set (match_operand:SI 0 "register_operand" "=r,r")
	(zero_extend:SI (match_operand:HI 1 "nonimmediate_operand" "0,m")))]
  ""
  "@
   movt %0, 0
   ldrh %0,%c1")


;; Compare instructions.

(define_insn "cmpsi_cc_insn"
  [(set (reg:CC CC_REGNUM)
	(compare:CC (match_operand:SI 0 "add_reg_operand" "r,r")
		    (match_operand:SI 1 "arith_operand" "r,L")))
   (clobber (match_scratch:SI 2 "=r,r"))]
  ""
  "sub %2,%0,%1"
  [(set_attr "type" "compare")])

(define_insn "sub_f"
  [(set (reg:CC CC_REGNUM)
        (compare:CC (match_operand:SI 1 "gpr_operand"  "r,r")
                    (match_operand:SI 2 "arith_operand" "r,L")))
   (set (match_operand:SI 0 "gpr_operand" "=r,r")
        (minus:SI (match_dup 1) (match_dup 2)))]
  ""
  "sub %0,%1,%2"
  [(set_attr "type" "compare")])

(define_insn "*sub_f_add_imm"
  [(set (reg:CC CC_REGNUM)
        (compare:CC (match_operand:SI 1 "gpr_operand"  "r")
                    (match_operand:SI 2 "arith_int_operand" "L")))
   (set (match_operand:SI 0 "gpr_operand" "=r")
        (plus:SI (match_dup 1) (match_operand:SI 3 "const_int_operand" "L")))]
  "INTVAL (operands[2]) == -INTVAL (operands[3])"
  "sub %0,%1,%2"
  [(set_attr "type" "compare")])

(define_expand "abssi2"
  [(set (match_dup 2) (const_int 0))
   (parallel [(set (reg:CC CC_REGNUM)
		   (compare:CC (match_dup 2)
			       (match_operand:SI 1 "nonmemory_operand" "")))
	      (set (match_dup 3)
		   (minus:SI (match_dup 2) (match_dup 1)))])
   (set (match_operand:SI 0 "gpr_operand" "=r")
	(if_then_else:SI (gt:SI (reg:CC CC_REGNUM) (const_int 0))
			 (match_dup 3)
			 (match_dup 1)))]
  "TARGET_CMOVE"
  "operands[2] = gen_reg_rtx (SImode); operands[3] = gen_reg_rtx (SImode);")

(define_insn "*add_c"
  [(set (reg:CC_C_LTU CC_REGNUM)
        (compare:CC_C_LTU
	  (plus:SI (match_operand:SI 1 "gpr_operand" "%r,r")
		   (match_operand:SI 2 "arith_operand" "r,L"))
	  (match_dup 1)))
   (set (match_operand:SI 0 "gpr_operand" "=r,r")
        (plus:SI (match_dup 1) (match_dup 2)))]
  ""
  "add %0,%1,%2"
  [(set_attr "type" "compare")])

(define_insn "*add_c_rev"
  [(set (reg:CC_C_LTU CC_REGNUM)
        (compare:CC_C_LTU
	  (plus:SI (match_operand:SI 1 "gpr_operand" "%r,r")
		   (match_operand:SI 2 "arith_operand" "r,L"))
	  (match_dup 1)))
   (set (match_operand:SI 0 "gpr_operand" "=r,r")
        (plus:SI (match_dup 2) (match_dup 1)))]
  ""
  "add %0,%1,%2"
  [(set_attr "type" "compare")])

(define_insn "*sub_c"
  [(set (reg:CC_C_GTU CC_REGNUM)
        (compare:CC_C_GTU
	  (minus:SI (match_operand:SI 1 "gpr_operand"  "r,r")
		    (match_operand:SI 2 "arith_operand" "r,L"))
	  (match_dup 1)))
   (set (match_operand:SI 0 "gpr_operand" "=r,r")
        (minus:SI (match_dup 1) (match_dup 2)))]
  ""
  "sub %0,%1,%2"
  [(set_attr "type" "compare")])

(define_insn "*sub_c_void"
  [(set (reg:CC_C_GTU CC_REGNUM)
        (compare:CC_C_GTU
	  (minus:SI (match_operand:SI 1 "gpr_operand"  "r,r")
		    (match_operand:SI 2 "arith_operand" "r,L"))
	  (match_dup 1)))
   (clobber (match_scratch:SI 0 "=r,r"))]
  ""
  "sub %0,%1,%2"
  [(set_attr "type" "compare")])

; floating point comparisons

(define_insn "*cmpsf_cc_insn"
  [(match_parallel 3 "float_operation"
     [(set (reg:CC_FP CCFP_REGNUM)
	   (compare:CC_FP (match_operand:SF 0 "gpr_operand" "r")
			  (match_operand:SF 1 "gpr_operand" "r")))
      (clobber (match_scratch:SF 2 "=r"))])]
  "!TARGET_SOFT_CMPSF"
  "fsub %2,%0,%1"
  [(set_attr "type" "fp")
   (set_attr "fp_mode" "round_unknown")])

;; ??? do we have to relax the operand0 predicate to immediate_operand
;; to allow the rtl loop optimizer to generate comparisons?  OTOH
;; we want call_address_operand to enforce valid operands so that
;; combine won't do silly things, allowing instruction scheduling to do
;; a proper job.
(define_insn "*cmpsf_eq"
  [(set (reg:CC_FP_EQ CC_REGNUM) (compare:CC_FP_EQ (reg:SF 0) (reg:SF 1)))
   (use (match_operand:SI 0 "call_address_operand" "Csy,r"))
   (clobber (reg:SI GPR_IP))
   (clobber (reg:SI GPR_LR))]
  "TARGET_SOFT_CMPSF"
  "%f0"
  [(set_attr "type" "sfunc")])

(define_insn "*cmpsf_gte"
  [(set (reg:CC_FP_GTE CC_REGNUM) (compare:CC_FP_GTE (reg:SF 0) (reg:SF 1)))
   (use (match_operand:SI 0 "call_address_operand" "Csy,r"))
   (clobber (reg:SI GPR_IP))
   (clobber (reg:SI GPR_LR))]
  "TARGET_SOFT_CMPSF"
  "%f0"
  [(set_attr "type" "sfunc")])

(define_insn "*cmpsf_ord"
  [(set (reg:CC_FP_ORD CC_REGNUM) (compare:CC_FP_ORD (reg:SF 0) (reg:SF 1)))
   (use (match_operand:SI 0 "call_address_operand" "Csy,r"))
   (clobber (reg:SI GPR_IP))
   (clobber (reg:SI GPR_16))
   (clobber (reg:SI GPR_LR))]
  "TARGET_SOFT_CMPSF"
  "%f0"
  [(set_attr "type" "sfunc")])

(define_insn "*cmpsf_uneq"
  [(set (reg:CC_FP_UNEQ CC_REGNUM) (compare:CC_FP_UNEQ (reg:SF 0) (reg:SF 1)))
   (use (match_operand:SI 0 "call_address_operand" "Csy,r"))
   (clobber (reg:SI GPR_IP))
   (clobber (reg:SI GPR_16))
   (clobber (reg:SI GPR_LR))]
  "TARGET_SOFT_CMPSF"
  "%f0"
  [(set_attr "type" "sfunc")])

;; conditional moves

(define_expand "mov<mode>cc"
  [(set (match_operand:WMODE 0 "gpr_operand" "")
	(if_then_else:WMODE (match_operand 1 "comparison_operator" "")
			    (match_operand:WMODE 2 "gpr_operand" "")
			    (match_operand:WMODE 3 "gpr_operand" "")))]
  "TARGET_CMOVE"
{
  rtx cmp_op0 = XEXP (operands[1], 0);
  rtx cmp_op1 = XEXP (operands[1], 1);
  enum machine_mode cmp_in_mode;
  enum rtx_code code = GET_CODE (operands[1]);

  cmp_in_mode = GET_MODE (cmp_op0);
  if (cmp_in_mode == VOIDmode)
    cmp_in_mode = GET_MODE (cmp_op1);
  if (cmp_in_mode == VOIDmode)
    cmp_in_mode = SImode;
  /* If the operands are a better match when reversed, swap them now.
     This allows combine to see the proper comparison codes.  */
  if (rtx_equal_p (operands[0], operands[2])
      && !rtx_equal_p (operands[0], operands[3]))
    {
      rtx tmp = operands[2]; operands[2] = operands[3]; operands[3] = tmp;
      code = (FLOAT_MODE_P (GET_MODE (cmp_op0))
	      ? reverse_condition_maybe_unordered (code)
	      : reverse_condition (code));
    }

  if (proper_comparison_operator (operands[1], VOIDmode))
    operands[1] = gen_rtx_fmt_ee (code, cmp_in_mode, cmp_op0, cmp_op1);
  else
    {
      if (!currently_expanding_to_rtl)
	{
	  /* ???  It would seem safest to FAIL here, but that would defeat
	     the purpose of having an if-conversion pass; its logic currently
	     assumes that the backend should be safe to insert condition code
	     setting instructions, as the same condition codes were presumably
	     set by the if-conversion input code.  */
	}
      /* What mode to give as first operand to gen_compare_reg here is
	 debatable.  VOIDmode would be minimalist; telling gen_compare_reg
	 to use the mode of CC_REGNUM (or putting it on the comparison
	 operator afterwards) is also a logical choice.  OTOH, by using
	 <MODE>mode, we have mode combine opportunities with flag setting
	 operations - if we get some.  */
      operands[1]
	= gen_compare_reg (<MODE>mode, code, cmp_in_mode, cmp_op0, cmp_op1);
    }
})

(define_insn "*mov<mode>cc_insn"
  [(set (match_operand:WMODE 0 "gpr_operand" "=r")
	(if_then_else:WMODE (match_operator 3 "proper_comparison_operator"
			      [(match_operand 4 "cc_operand") (const_int 0)])
			    (match_operand:WMODE 1 "gpr_operand" "r")
			    (match_operand:WMODE 2 "gpr_operand" "0")))]
  "TARGET_CMOVE"
  "mov%d3 %0,%1"
  [(set_attr "type" "cmove")])

(define_peephole2
  [(parallel [(set (match_operand:WMODE 0 "gpr_operand" "")
		   (match_operand:WMODE 1 "" ""))
	      (clobber (match_operand 8 "cc_operand"))])
   (match_operand 2 "" "")
   (set (match_operand:WMODE2 3 "gpr_operand" "")
	(match_operand:WMODE2 9 "gpr_operand" ""))
   (set (match_dup 3)
	(if_then_else:WMODE2 (match_operator 5 "proper_comparison_operator"
			       [(match_operand 6 "cc_operand")
				(match_operand 7 "const0_operand")])
			     (match_operand:WMODE2 4 "nonmemory_operand" "")
			     (match_dup 3)))]
  "REGNO (operands[0]) == REGNO (operands[9])
   && peep2_reg_dead_p (3, operands[0])
   && !reg_set_p (operands[0], operands[2])
   && !reg_set_p (operands[3], operands[2])
   && !reg_overlap_mentioned_p (operands[3], operands[2])"
  [(parallel [(set (match_dup 10) (match_dup 1))
	      (clobber (match_dup 8))])
   (match_dup 2)
   (set (match_dup 3)
	(if_then_else:WMODE2 (match_dup 5) (match_dup 4) (match_dup 3)))]
{
  operands[10] = simplify_gen_subreg (<WMODE:MODE>mode, operands[3],
				      <WMODE2:MODE>mode, 0);
  replace_rtx (operands[2], operands[9], operands[3]);
  replace_rtx (operands[2], operands[0], operands[10]);
  gcc_assert (!reg_overlap_mentioned_p (operands[0], operands[2]));
})

(define_peephole2
  [(parallel [(set (match_operand 6 "cc_operand") (match_operand 2 "" ""))
	      (set (match_operand:WMODE 0 "gpr_operand" "")
		   (match_operand:WMODE 1 "" ""))])
   (set (match_operand:WMODE2 3 "gpr_operand" "")
	(match_operand:WMODE2 4 "gpr_operand"))
   (set (match_dup 3)
	(if_then_else:WMODE2 (match_operator 5 "proper_comparison_operator"
			       [(match_dup 6)
			       (match_operand:WMODE 7 "const0_operand")])
			    (match_operand:WMODE2 8 "gpr_operand")
			    (match_dup 3)))]
  "REGNO (operands[0]) == REGNO (operands[8])
   && REVERSIBLE_CC_MODE (GET_MODE (operands[6]))
   && peep2_reg_dead_p (3, operands[6])
   && peep2_reg_dead_p (3, operands[0])
   && !reg_overlap_mentioned_p (operands[4], operands[3])"
  [(parallel [(set (match_dup 6) (match_dup 2))
	      (set (match_dup 9) (match_dup 1))])
   (set (match_dup 3)
	(if_then_else:WMODE2 (match_dup 5) (match_dup 4) (match_dup 3)))]
  "
{
  operands[5]
    = gen_rtx_fmt_ee (REVERSE_CONDITION (GET_CODE (operands[5]),
					 GET_MODE (operands[6])),
		      GET_MODE (operands[5]), operands[6], operands[7]);
  operands[9] = simplify_gen_subreg (<WMODE:MODE>mode, operands[3],
				     <WMODE2:MODE>mode, 0);
}")

;; These control RTL generation for conditional jump insns

;; To signal to can_compare_p that the cbranchs?4 patterns work,
;; they must allow const0_rtx for both comparison operands
(define_expand "cbranchsi4"
  [(set (reg CC_REGNUM)
	(compare (match_operand:SI 1 "add_operand" "")
		 (match_operand:SI 2 "arith_operand" "")))
   (set (pc)
	(if_then_else
	      (match_operator 0 "ordered_comparison_operator" [(reg CC_REGNUM)
							       (const_int 0)])
	      (label_ref (match_operand 3 "" ""))
	      (pc)))]
  ""
{
  rtx cmp = gen_compare_reg (VOIDmode, GET_CODE (operands[0]), SImode,
			     operands[1], operands[2]);
  emit_jump_insn (gen_branch_insn (operands[3], cmp, XEXP (cmp, 0)));
  DONE;
})

(define_expand "cbranchsf4"
  [(set (reg CC_REGNUM)
	(compare (match_operand:SF 1 "arith_operand" "")
		 (match_operand:SF 2 "arith_operand" "")))
   (set (pc)
	(if_then_else
	      (match_operator 0 "comparison_operator" [(reg CC_REGNUM)
						       (const_int 0)])
	      (label_ref (match_operand 3 "" ""))
	      (pc)))]
  ""
{
  rtx cmp = gen_compare_reg (VOIDmode, GET_CODE (operands[0]), SFmode,
			     operands[1], operands[2]);
  emit_jump_insn (gen_branch_insn (operands[3], cmp, XEXP (cmp, 0)));
  DONE;
})

;; Now match both normal and inverted jump.

(define_insn "branch_insn"
  [(set (pc)
	(if_then_else (match_operator 1 "proper_comparison_operator"
				      [(match_operand 2 "cc_operand")
				       (const_int 0)])
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "b%d1 %l0"
  [(set_attr "type" "branch")])

(define_insn "*rev_branch_insn"
  [(set (pc)
	(if_then_else (match_operator 1 "proper_comparison_operator"
				      [(reg CC_REGNUM) (const_int 0)])
		      (pc)
		      (label_ref (match_operand 0 "" ""))))]
  ""
  "b%D1 %l0"
  [(set_attr "type" "branch")])

;; Unconditional and other jump instructions.

(define_insn "jump"
  [(set (pc) (label_ref (match_operand 0 "" "")))]
  ""
  "b %l0"
  [(set_attr "type" "uncond_branch")])

(define_insn "indirect_jump"
  [(set (pc) (match_operand:SI 0 "gpr_operand" "r"))]
  ""
  "jr %0"
  [(set_attr "type" "uncond_branch")])

(define_expand "tablejump"
  [(parallel [(set (pc) (match_operand:SI 0 "gpr_operand" ""))
              (use (label_ref (match_operand 1 "" "")))])]
  ""
{
  /* In PIC mode, the table entries are stored PC relative.
     Convert the relative address to an absolute address.  */
  if (flag_pic)
    {
      rtx op1 = gen_rtx_LABEL_REF (Pmode, operands[1]);

      operands[0] = expand_simple_binop (Pmode, PLUS, operands[0],
					 op1, NULL_RTX, 0, OPTAB_DIRECT);
    }
})

(define_insn "*tablejump_internal"
  [(set (pc) (match_operand:SI 0 "gpr_operand" "r"))
   (use (label_ref (match_operand 1 "" "")))]
  ""
  "jr %0;"
  [(set_attr "type" "uncond_branch")])

(define_insn "*tablejump_hi_internal"
  [(set (pc) (match_operand:HI 0 "gpr_operand" "r"))
   (use (label_ref (match_operand 1 "" "")))]
  "optimize_size && TARGET_SMALL16"
  "jr %0;"
  [(set_attr "type" "uncond_branch")])


(define_expand "call"
  ;; operands[1] is stack_size_rtx
  ;; operands[2] is next_arg_register
  [(parallel [(call (match_operand:SI 0 "call_operand" "")
		    (match_operand 1 "" ""))
	     (clobber (reg:SI GPR_LR))])]
  ""
{
  bool target_uninterruptible = epiphany_call_uninterruptible_p (operands[0]);

  if (!call_operand (operands[1], VOIDmode))
    operands[0]
      = change_address (operands[0], VOIDmode,
			copy_to_mode_reg (Pmode, XEXP (operands[0], 0)));
  if (epiphany_uninterruptible_p (current_function_decl)
      != target_uninterruptible)
    {
      emit_insn (target_uninterruptible ? gen_gid (): gen_gie ());
      emit_call_insn
	(gen_rtx_PARALLEL
	  (VOIDmode,
	   gen_rtvec (2, gen_rtx_CALL (VOIDmode, operands[0], operands[1]),
			 gen_rtx_CLOBBER (VOIDmode,
					  gen_rtx_REG (SImode, GPR_LR)))));
      emit_insn (target_uninterruptible ? gen_gie (): gen_gid ());
      DONE;
    }
})

(define_insn "*call_i"
  [(match_parallel 2 "float_operation"
     [(call (mem:SI (match_operand:SI 0 "call_address_operand" "Csy,r"))
	    (match_operand 1 "" ""))
      (clobber (reg:SI GPR_LR))])]
  ""
  "%f0"
  [(set_attr "type" "call")])

(define_expand "sibcall"
  ;; operands[1] is stack_size_rtx
  ;; operands[2] is next_arg_register
  [(parallel [(call (match_operand:SI 0 "call_operand" "")
		    (match_operand 1 "" ""))
	     (return)])]
  ""
{
  bool target_uninterruptible = epiphany_call_uninterruptible_p (operands[0]);

  if (!call_operand (operands[1], VOIDmode))
    operands[0]
      = change_address (operands[0], VOIDmode,
			copy_to_mode_reg (Pmode, XEXP (operands[0], 0)));
  if (epiphany_uninterruptible_p (current_function_decl)
      != target_uninterruptible)
    {
      emit_insn (target_uninterruptible ? gen_gid (): gen_gie ());
      emit_call_insn
	(gen_rtx_PARALLEL
	  (VOIDmode,
	   gen_rtvec (2, gen_rtx_CALL (VOIDmode, operands[0], operands[1]),
			 ret_rtx)));
      emit_insn (target_uninterruptible ? gen_gie (): gen_gid ());
      DONE;
    }
})

(define_insn "*sibcall_i"
  [(call (mem:SI (match_operand:SI 0 "call_address_operand" "Csy,Rsc"))
	 (match_operand 1 "" ""))
   (return)]
  ""
  "@
   b %0
   jr %0"
  [(set_attr "type" "call")])

(define_expand "call_value"
  ;; operand 2 is stack_size_rtx
  ;; operand 3 is next_arg_register
  [(parallel [(set (match_operand 0 "gpr_operand" "=r")
		   (call (match_operand:SI 1 "call_operand" "")
			 (match_operand 2 "" "")))
	     (clobber (reg:SI GPR_LR))])]
  ""
{
  bool target_uninterruptible = epiphany_call_uninterruptible_p (operands[1]);

  if (!call_operand (operands[1], VOIDmode))
    operands[1]
      = change_address (operands[1], VOIDmode,
			copy_to_mode_reg (Pmode, XEXP (operands[1], 0)));
  if (epiphany_uninterruptible_p (current_function_decl)
      != target_uninterruptible)
    {
      emit_insn (target_uninterruptible ? gen_gid (): gen_gie ());
      emit_call_insn
	(gen_rtx_PARALLEL
	  (VOIDmode,
	   gen_rtvec (2, gen_rtx_SET
			   (VOIDmode, operands[0],
			    gen_rtx_CALL (VOIDmode, operands[1], operands[2])),
			 gen_rtx_CLOBBER (VOIDmode,
					  gen_rtx_REG (SImode, GPR_LR)))));
      emit_insn (target_uninterruptible ? gen_gie (): gen_gid ());
      DONE;
    }
})

(define_insn "*call_value_i"
  [(match_parallel 3 "float_operation"
     [(set (match_operand 0 "gpr_operand" "=r,r")
	   (call (mem:SI (match_operand:SI 1 "call_address_operand" "Csy,r"))
	         (match_operand 2 "" "")))
      (clobber (reg:SI GPR_LR))])]
  ""
  "%f1"
  [(set_attr "type" "call")
   (set_attr "length" "4")])

(define_expand "sibcall_value"
  ;; operand 2 is stack_size_rtx
  ;; operand 3 is next_arg_register
  [(parallel [(set (match_operand 0 "gpr_operand" "=r")
		   (call (match_operand:SI 1 "call_operand" "")
			 (match_operand 2 "" "")))
	     (return)])]
  ""
{
  bool target_uninterruptible = epiphany_call_uninterruptible_p (operands[1]);

  if (!call_operand (operands[1], VOIDmode))
    operands[1]
      = change_address (operands[1], VOIDmode,
			copy_to_mode_reg (Pmode, XEXP (operands[1], 0)));
  if (epiphany_uninterruptible_p (current_function_decl)
      != target_uninterruptible)
    {
      emit_insn (target_uninterruptible ? gen_gid (): gen_gie ());
      emit_call_insn
	(gen_rtx_PARALLEL
	  (VOIDmode,
	   gen_rtvec (2, gen_rtx_SET
			   (VOIDmode, operands[0],
			    gen_rtx_CALL (VOIDmode, operands[1], operands[2])),
			 ret_rtx)));
      emit_insn (target_uninterruptible ? gen_gie (): gen_gid ());
      DONE;
    }
})

(define_insn "*sibcall_value_i"
  [(set (match_operand 0 "gpr_operand" "=r,r")
	(call (mem:SI (match_operand:SI 1 "call_address_operand" "Csy,Rsc"))
	      (match_operand 2 "" "")))
   (return)]
  ""
  "@
   b %1
   jr %1"
  [(set_attr "type" "call")
   (set_attr "length" "4")])

(define_expand "prologue"
  [(pc)]
  ""
{
  epiphany_expand_prologue ();
  DONE;
})

(define_expand "epilogue"
  [(pc)]
  ""
{
  epiphany_expand_epilogue (0);
  DONE;
})

(define_expand "sibcall_epilogue"
  [(pc)]
  ""
{
  epiphany_expand_epilogue (1);
  DONE;
})

; Since the demise of REG_N_SETS, it is no longer possible to find out
; in the prologue / epilogue expanders how many times lr is set.
; Using df_regs_ever_live_p to decide if lr needs saving means that
; any explicit use of lr will cause it to be saved; hence we cannot
; represent the blink use in return / sibcall instructions themselves, and
; instead have to show it in EPILOGUE_USES.
(define_insn "return_i"
  [(return)]
  "reload_completed"
  "rts"
  [(set_attr "type" "uncond_branch")])

(define_insn "return_internal_interrupt"
  [(return)
   (unspec_volatile [(const_int 0)] 1)]
  ""
  "rti"
  [(set_attr "type" "uncond_branch")])

(define_insn "stack_adjust_add"
  [(set (reg:SI GPR_SP)
	(plus:SI (reg:SI GPR_SP) (match_operand:SI 0 "arith_operand" "rL")))
   (clobber (reg:CC CC_REGNUM))
   (clobber (reg:SI STATUS_REGNUM))
   (clobber (match_operand:BLK 1 "memory_operand" "=m"))]
  "reload_completed"
  "add sp,sp,%0")

(define_insn "stack_adjust_mov"
  [(set (reg:SI GPR_SP) (reg:SI GPR_FP))
   (clobber (match_operand:BLK 0 "memory_operand" "=m"))]
  "reload_completed"
  "mov sp,fp"
  [(set_attr "type" "move")])

(define_insn "stack_adjust_str"
  [(set (match_operand 0 "stacktop_operand" "=m")
	(match_operand 1 "any_gpr_operand" "r"))
   (set (reg:SI GPR_SP)
	(plus:SI (reg:SI GPR_SP) (match_operand:SI 2 "nonmemory_operand" "rn")))
   (clobber (match_operand:BLK 3 "memory_operand" "=m"))]
  "reload_completed"
{
  return (GET_MODE_SIZE (GET_MODE (operands[0])) <= 4
	  ? \"str %1,%0,%C2\" : \"strd %1,%0,%X2\");
}
  [(set_attr "type" "store")])

(define_insn "stack_adjust_ldr"
  [(set (match_operand:SI 0 "gpr_operand" "=r")
	(match_operand:SI 1 "stacktop_operand" "m"))
   (set (reg:SI GPR_SP)
	(plus:SI (reg:SI GPR_SP) (match_operand:SI 2 "nonmemory_operand" "rn")))
   (clobber (match_operand:BLK 3 "memory_operand" "=m"))]
  "reload_completed"
  "ldr %0,%1,%C2"
  [(set_attr "type" "load")])

;; Define some fake vector operations so that the vectorizer is happy to use
;; 64 bit loads/stores.
(define_expand "vec_unpacks_lo_v4hi"
  [(match_operand:V2SI 0 "gpr_operand")
   (match_operand:V4HI 1 "gpr_operand")]
  ""
{
  rtx in = simplify_gen_subreg (SImode, operands[1], V4HImode, 0);
  rtx outl = simplify_gen_subreg (SImode, operands[0], V2SImode, 0);
  rtx outh
    = simplify_gen_subreg (SImode, operands[0], V2SImode, UNITS_PER_WORD);

  if (reg_overlap_mentioned_p (outl, in))
    in = copy_to_mode_reg (SImode, in);
  emit_insn (gen_ashlsi3 (outl, in, GEN_INT (16)));
  emit_insn (gen_ashrsi3 (outl, outl, GEN_INT (16)));
  emit_insn (gen_ashrsi3 (outh, in, GEN_INT (16)));
  DONE;
})

(define_expand "vec_unpacks_hi_v4hi"
  [(match_operand:V2SI 0 "gpr_operand")
   (match_operand:V4HI 1 "gpr_operand")]
  ""
{
  rtx in = simplify_gen_subreg (SImode, operands[1], V4HImode, UNITS_PER_WORD);
  rtx outl = simplify_gen_subreg (SImode, operands[0], V2SImode, 0);
  rtx outh
    = simplify_gen_subreg (SImode, operands[0], V2SImode, UNITS_PER_WORD);

  if (reg_overlap_mentioned_p (outl, in))
    in = copy_to_mode_reg (SImode, in);
  emit_insn (gen_ashlsi3 (outl, in, GEN_INT (16)));
  emit_insn (gen_ashrsi3 (outl, outl, GEN_INT (16)));
  emit_insn (gen_ashrsi3 (outh, in, GEN_INT (16)));
  DONE;
})

(define_code_iterator addsub [plus minus])

(define_code_iterator alu_binop
  [plus minus and ior xor])

(define_code_attr insn_opname
  [(plus "add") (minus "sub") (mult "mul") (div "div")
   (and "and") (ior "ior") (xor "xor")])

; You might think that this would work better as a define_expand, but
; again lower_subreg pessimizes the code if it sees indiviudual operations.
; We need to keep inputs and outputs as register pairs if we want to
; get sensible register allocation for double-word load and store operations.
(define_insn_and_split "<insn_opname>v2si3"
  [(set (match_operand:V2SI 0 "gpr_operand" "=r")
	(alu_binop:V2SI (match_operand:V2SI 1 "gpr_operand" "r")
			(match_operand:V2SI 2 "gpr_operand" "r")))
   (clobber (reg:CC CC_REGNUM))]
  ""
  "#"
  "reload_completed || (epiphany_vect_align == 4 && TARGET_SPLIT_VECMOVE_EARLY)"
  [(const_int 0)]
{
  rtx o0l, o0h, o1l, o1h, o2l, o2h;

  o0l = simplify_gen_subreg (SImode, operands[0], V2SImode, 0);
  o0h = simplify_gen_subreg (SImode, operands[0], V2SImode, UNITS_PER_WORD);
  o1l = simplify_gen_subreg (SImode, operands[1], V2SImode, 0);
  o1h = simplify_gen_subreg (SImode, operands[1], V2SImode, UNITS_PER_WORD);
  o2l = simplify_gen_subreg (SImode, operands[2], V2SImode, 0);
  o2h = simplify_gen_subreg (SImode, operands[2], V2SImode, UNITS_PER_WORD);
  if (reg_overlap_mentioned_p (o0l, o1h))
    o1h = copy_to_mode_reg (SImode, o1h);
  if (reg_overlap_mentioned_p (o0l, o2h))
    o2h = copy_to_mode_reg (SImode, o2h);
  emit_insn (gen_<insn_opname>si3 (o0l, o1l, o2l));
  emit_insn (gen_<insn_opname>si3 (o0h, o1h, o2h));
  DONE;
}
  [(set_attr "length" "8")])

(define_expand "<insn_opname>v2sf3"
  [(parallel
     [(set (match_operand:V2SF 0 "gpr_operand" "")
	   (addsub:V2SF (match_operand:V2SF 1 "gpr_operand" "")
			(match_operand:V2SF 2 "gpr_operand" "")))
      (clobber (reg:CC_FP CCFP_REGNUM))])])

(define_insn_and_split "<insn_opname>v2sf3_i"
  [(match_parallel 3 "float_operation"
     [(set (match_operand:V2SF 0 "gpr_operand" "=r")
	   (addsub:V2SF (match_operand:V2SF 1 "gpr_operand" "r")
			(match_operand:V2SF 2 "gpr_operand" "r")))
      (clobber (reg:CC_FP CCFP_REGNUM))])]
  ""
  "#"
  "reload_completed || (epiphany_vect_align == 4 && TARGET_SPLIT_VECMOVE_EARLY)"
  [(parallel
     [(set (match_dup 4) (addsub:SF (match_dup 5) (match_dup 6)))
      (clobber (reg:CC_FP CCFP_REGNUM))
      (match_dup 10)
      (match_dup 11)])
   (parallel
     [(set (match_dup 7) (addsub:SF (match_dup 8) (match_dup 9)))
      (clobber (reg:CC_FP CCFP_REGNUM))
      (match_dup 10)
      (match_dup 11)])]
{
  operands[4] = simplify_gen_subreg (SFmode, operands[0], V2SFmode, 0);
  operands[5] = simplify_gen_subreg (SFmode, operands[1], V2SFmode, 0);
  operands[6] = simplify_gen_subreg (SFmode, operands[2], V2SFmode, 0);
  operands[7]
    = simplify_gen_subreg (SFmode, operands[0], V2SFmode, UNITS_PER_WORD);
  operands[8]
    = simplify_gen_subreg (SFmode, operands[1], V2SFmode, UNITS_PER_WORD);
  operands[9]
    = simplify_gen_subreg (SFmode, operands[2], V2SFmode, UNITS_PER_WORD);
  if (!reload_completed)
    {
      if (reg_overlap_mentioned_p (operands[4], operands[8]))
	operands[8] = copy_to_mode_reg (SFmode, operands[8]);
      if (reg_overlap_mentioned_p (operands[4], operands[9]))
	operands[9] = copy_to_mode_reg (SFmode, operands[9]);
      emit_insn (gen_<insn_opname>sf3 (operands[4], operands[5], operands[6]));
      emit_insn (gen_<insn_opname>sf3 (operands[7], operands[8], operands[9]));
      DONE;
    }
  gcc_assert (!reg_overlap_mentioned_p (operands[4], operands[8]));
  gcc_assert (!reg_overlap_mentioned_p (operands[4], operands[9]));
  operands[10] = XVECEXP (operands[3], 0, XVECLEN (operands[3], 0) - 2);
  operands[11] = XVECEXP (operands[3], 0, XVECLEN (operands[3], 0) - 1);
}
  [(set_attr "length" "8")
   (set_attr "type" "v2fp")])

(define_expand "mul<mode>3"
  [(parallel
     [(set (match_operand:DWV2MODE 0 "gpr_operand" "")
	   (mult:DWV2MODE (match_operand:DWV2MODE 1 "gpr_operand" "")
			  (match_operand:DWV2MODE 2 "gpr_operand" "")))
      (clobber (reg:CC_FP CCFP_REGNUM))])])

(define_insn_and_split "mul<mode>3_i"
  [(match_parallel 3 "float_operation"
     [(set (match_operand:DWV2MODE 0 "gpr_operand" "=r")
	   (mult:DWV2MODE (match_operand:DWV2MODE 1 "gpr_operand" "r")
			  (match_operand:DWV2MODE 2 "gpr_operand" "r")))
      (clobber (reg:CC_FP CCFP_REGNUM))])]
  ""
  "#"
  "reload_completed || (epiphany_vect_align == 4 && TARGET_SPLIT_VECMOVE_EARLY)"
  [(parallel
     [(set (match_dup 4) (mult:<vmode_PART> (match_dup 5) (match_dup 6)))
      (clobber (reg:CC_FP CCFP_REGNUM))
      (match_dup 10)
      (match_dup 11)])
   (parallel
     [(set (match_dup 7) (mult:<vmode_PART> (match_dup 8) (match_dup 9)))
      (clobber (reg:CC_FP CCFP_REGNUM))
      (match_dup 10)
      (match_dup 11)])]
{
  operands[4]
    = simplify_gen_subreg (<vmode_PART>mode, operands[0], <MODE>mode, 0);
  operands[5]
    = simplify_gen_subreg (<vmode_PART>mode, operands[1], <MODE>mode, 0);
  operands[6]
    = simplify_gen_subreg (<vmode_PART>mode, operands[2], <MODE>mode, 0);
  operands[7] = simplify_gen_subreg (<vmode_PART>mode, operands[0],
				     <MODE>mode, UNITS_PER_WORD);
  operands[8] = simplify_gen_subreg (<vmode_PART>mode, operands[1],
				     <MODE>mode, UNITS_PER_WORD);
  operands[9] = simplify_gen_subreg (<vmode_PART>mode, operands[2],
				     <MODE>mode, UNITS_PER_WORD);
  if (!reload_completed)
    {
      if (reg_overlap_mentioned_p (operands[4], operands[8]))
	operands[8] = copy_to_mode_reg (<vmode_PART>mode, operands[8]);
      if (reg_overlap_mentioned_p (operands[4], operands[9]))
	operands[9] = copy_to_mode_reg (<vmode_PART>mode, operands[9]);
      emit_insn (gen_mul<vmode_part>3 (operands[4], operands[5], operands[6]));
      emit_insn (gen_mul<vmode_part>3 (operands[7], operands[8], operands[9]));
      DONE;
    }
  gcc_assert (!reg_overlap_mentioned_p (operands[4], operands[8]));
  gcc_assert (!reg_overlap_mentioned_p (operands[4], operands[9]));
  operands[10] = XVECEXP (operands[3], 0, XVECLEN (operands[3], 0) - 2);
  operands[11] = XVECEXP (operands[3], 0, XVECLEN (operands[3], 0) - 1);
}
  [(set_attr "length" "8")
   (set_attr "type" "<vmode_fp_type>")])

(define_insn_and_split "*fmadd<mode>_combine"
  [(match_parallel 4 "float_operation"
     [(set (match_operand:DWV2MODE 0 "gpr_operand" "=r")
	   (plus:DWV2MODE (mult:<MODE>
				(match_operand:<MODE> 1 "gpr_operand" "r")
				(match_operand:<MODE> 2 "gpr_operand" "r"))
			  (match_operand:<MODE> 3 "gpr_operand" "0")))
      (clobber (reg:CC_FP CCFP_REGNUM))])]
  "TARGET_FUSED_MADD || <MODE>mode == V2SImode"
  "#"
  "reload_completed || (epiphany_vect_align == 4 && TARGET_SPLIT_VECMOVE_EARLY)"
  [(parallel
     [(set (match_dup 5)
	   (plus:<vmode_PART> (mult:<vmode_PART> (match_dup 6) (match_dup 7))
			      (match_dup 8)))
      (clobber (reg:CC_FP CCFP_REGNUM))
      (match_dup 13)
      (match_dup 14)])
   (parallel
     [(set (match_dup 9)
	   (plus:<vmode_PART> (mult:<vmode_PART> (match_dup 10) (match_dup 11))
			      (match_dup 12)))
      (clobber (reg:CC_FP CCFP_REGNUM))
      (match_dup 13)
      (match_dup 14)])]
{
  operands[5]
    = simplify_gen_subreg (<vmode_PART>mode, operands[0], <MODE>mode, 0);
  operands[6]
    = simplify_gen_subreg (<vmode_PART>mode, operands[1], <MODE>mode, 0);
  operands[7]
    = simplify_gen_subreg (<vmode_PART>mode, operands[2], <MODE>mode, 0);
  operands[8]
    = simplify_gen_subreg (<vmode_PART>mode, operands[3], <MODE>mode, 0);
  operands[9] = simplify_gen_subreg (<vmode_PART>mode, operands[0],
				     <MODE>mode, UNITS_PER_WORD);
  operands[10] = simplify_gen_subreg (<vmode_PART>mode, operands[1],
				      <MODE>mode, UNITS_PER_WORD);
  operands[11] = simplify_gen_subreg (<vmode_PART>mode, operands[2],
				      <MODE>mode, UNITS_PER_WORD);
  operands[12] = simplify_gen_subreg (<vmode_PART>mode, operands[3],
				      <MODE>mode, UNITS_PER_WORD);
  if (!reload_completed)
    {
      if (reg_overlap_mentioned_p (operands[5], operands[10]))
	operands[10] = copy_to_mode_reg (<vmode_PART>mode, operands[10]);
      if (reg_overlap_mentioned_p (operands[5], operands[11]))
	operands[11] = copy_to_mode_reg (<vmode_PART>mode, operands[11]);
      if (reg_overlap_mentioned_p (operands[5], operands[12]))
	operands[12] = copy_to_mode_reg (<vmode_PART>mode, operands[12]);
      emit_insn (gen_madd<vmode_part> (operands[5], operands[6], operands[7],
				       operands[8]));
      emit_insn (gen_madd<vmode_part> (operands[9], operands[10], operands[11],
				       operands[12]));
      DONE;
    }
  gcc_assert (!reg_overlap_mentioned_p (operands[5], operands[10]));
  gcc_assert (!reg_overlap_mentioned_p (operands[5], operands[11]));
  gcc_assert (!reg_overlap_mentioned_p (operands[5], operands[12]));
  operands[13] = XVECEXP (operands[4], 0, XVECLEN (operands[4], 0) - 2);
  operands[14] = XVECEXP (operands[4], 0, XVECLEN (operands[4], 0) - 1);
}
  [(set_attr "length" "8")
   (set_attr "type" "<vmode_fp_type>")])

(define_expand "vec_set<mode>"
  [(match_operand:DWV2MODE 0 "register_operand")
   (match_operand:<vmode_PART> 1 "register_operand")
   (match_operand 2 "const_int_operand" "")]
  ""
{
  operands[0]
    = simplify_gen_subreg (<vmode_PART>mode, operands[0], <MODE>mode,
			   UNITS_PER_WORD * INTVAL (operands[2]));
  emit_move_insn (operands[0], operands[1]);
  DONE;
})

(define_expand "movmisalign<mode>"
 [(set (match_operand:DWV2MODE 0 "nonimmediate_operand" "")
       (match_operand:DWV2MODE 1 "general_operand" ""))]
 ""
{
  rtx op00, op01, op10, op11;

  op00 = simplify_gen_subreg (<vmode_PART>mode, operands[0], <MODE>mode, 0);
  op01 = simplify_gen_subreg (<vmode_PART>mode, operands[0], <MODE>mode,
			      UNITS_PER_WORD);
  op10 = simplify_gen_subreg (<vmode_PART>mode, operands[1], <MODE>mode, 0);
  op11 = simplify_gen_subreg (<vmode_PART>mode, operands[1], <MODE>mode,
			      UNITS_PER_WORD);
  emit_move_insn (op00, op10);
  emit_move_insn (op01, op11);
  DONE;
})

(define_insn "nop"
  [(const_int 0)]
  ""
  "nop"
  [(set_attr "type" "flow")])
