diff options
Diffstat (limited to 'gcc/config/epiphany/epiphany.md')
-rwxr-xr-x | gcc/config/epiphany/epiphany.md | 2447 |
1 files changed, 2447 insertions, 0 deletions
diff --git a/gcc/config/epiphany/epiphany.md b/gcc/config/epiphany/epiphany.md new file mode 100755 index 00000000000..c8354e8eddb --- /dev/null +++ b/gcc/config/epiphany/epiphany.md @@ -0,0 +1,2447 @@ +;; Machine description of the Adaptiva epiphany cpu for GNU C compiler +;; Copyright (C) 1994, 1997, 1998, 1999, 2000, 2004, 2005, 2007, 2009, 2010, +;; 2011 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,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,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 (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) + && (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]) && !MEM_P (operands[1])) + || 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 (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 (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 + 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_insn "subsi3" + [(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" "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 (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_insn "*iadd" + [(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_insn "*isub" + [(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")]) + +(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))])] + "" + "imsub %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 "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_op0); + 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" "fp")]) + +(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_insn "nop" + [(const_int 0)] + "" + "nop" + [(set_attr "type" "flow")]) |