| ;; GCC machine description for NEC V850 |
| ;; Copyright (C) 1996-2021 Free Software Foundation, Inc. |
| ;; Contributed by Jeff Law (law@cygnus.com). |
| |
| ;; 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/>. |
| |
| ;; The original PO technology requires these to be ordered by speed, |
| ;; so that assigner will pick the fastest. |
| |
| ;; See file "rtl.def" for documentation on define_insn, match_*, et. al. |
| |
| ;; The V851 manual states that the instruction address space is 16M; |
| ;; the various branch/call instructions only have a 22bit offset (4M range). |
| ;; |
| ;; One day we'll probably need to handle calls to targets more than 4M |
| ;; away. |
| ;; |
| |
| ;; Condition codes |
| ;; |
| ;; Data movement (load, store, register copy) does not modify condition |
| ;; codes. But there is no way to add two registers together without |
| ;; modifying the condition codes. |
| ;; |
| ;; So we do not expose the condition codes until after reload. The |
| ;; general approach is to have a define_insn_and_split for the basic |
| ;; operation with no condition codes in the pattern (to give the |
| ;; optimizers maximal freedom). The splitter adds a clobber of the |
| ;; condition codes after reload. There is a distinct pattern which |
| ;; sets the condition codes. |
| ;; |
| ;; As noted, data movement does not affect condition codes. |
| ;; |
| ;; Arithmetic generally set the codes in the expected ways, with mul |
| ;; instructions being a notable outlier. div instructions generally |
| ;; do the right thing, except when the output registers are the same |
| ;; when the flags do not get set. We just assume they're clobbered |
| ;; for div instructions to avoid MD bloat with marginal benefit |
| ;; |
| ;; The bit manipulation instructions (clr1, not1, set1) set condition |
| ;; codes, but not in a useful way (they're set to the prior status of |
| ;; the bit). So we just model those as clobbers. tst1 does set the |
| ;; condition codes in a useful way. We could perhaps do better with |
| ;; these by noting they only modify the Z flag, it doesn't seem worth |
| ;; the effort. |
| ;; |
| ;; Byte swaps seem to change the condition codes, but I haven't tried |
| ;; to describe how. |
| ;; |
| ;; I have no documentation on the rotate instructions. They likely |
| ;; set the condition codes, but I've left them as clobbers for now. |
| |
| |
| ;; The size of instructions in bytes. |
| |
| ;;--------------------------------------------------------------------------- |
| ;; Constants |
| |
| ;; |
| (define_constants |
| [(ZERO_REGNUM 0) ; constant zero |
| (SP_REGNUM 3) ; Stack Pointer |
| (GP_REGNUM 4) ; GP Pointer |
| (RV_REGNUM 10) ; Return value register |
| (EP_REGNUM 30) ; EP pointer |
| (LP_REGNUM 31) ; Return address register |
| (CC_REGNUM 32) ; Condition code pseudo register |
| (FCC_REGNUM 33) ; Floating Condition code pseudo register |
| ] |
| ) |
| |
| (define_c_enum "unspec" [ |
| UNSPEC_LOOP |
| UNSPEC_RCP |
| UNSPEC_RSQRT |
| ]) |
| |
| (define_attr "length" "" |
| (const_int 4)) |
| |
| (define_attr "long_calls" "yes,no" |
| (const (if_then_else (symbol_ref "TARGET_LONG_CALLS") |
| (const_string "yes") |
| (const_string "no")))) |
| |
| ;; Types of instructions (for scheduling purposes). |
| |
| (define_attr "type" "load,store,bit1,mult,macc,div,fpu,single,other" |
| (const_string "other")) |
| |
| (define_attr "cpu" "none,v850,v850e,v850e1,v850e2,v850e2v3,v850e3v5" |
| (cond [(match_test "TARGET_V850") |
| (const_string "v850") |
| (match_test "TARGET_V850E") |
| (const_string "v850e") |
| (match_test "TARGET_V850E1") |
| (const_string "v850e1") |
| (match_test "TARGET_V850E2") |
| (const_string "v850e2") |
| (match_test "TARGET_V850E2V3") |
| (const_string "v850e2v3") |
| (match_test "TARGET_V850E3V5") |
| (const_string "v850e3v5")] |
| (const_string "none"))) |
| |
| |
| ;; Function units for the V850. As best as I can tell, there's |
| ;; a traditional memory load/use stall as well as a stall if |
| ;; the result of a multiply is used too early. |
| |
| (define_insn_reservation "v850_other" 1 |
| (eq_attr "type" "other") |
| "nothing") |
| (define_insn_reservation "v850_mult" 2 |
| (eq_attr "type" "mult") |
| "nothing") |
| (define_insn_reservation "v850_memory" 2 |
| (eq_attr "type" "load") |
| "nothing") |
| |
| (include "predicates.md") |
| (include "constraints.md") |
| |
| ;; ---------------------------------------------------------------------- |
| ;; MOVE INSTRUCTIONS |
| ;; ---------------------------------------------------------------------- |
| ;; movdi |
| |
| (define_expand "movdi" |
| [(set (match_operand:DI 0 "general_operand") |
| (match_operand:DI 1 "general_operand"))] |
| "TARGET_V850E3V5_UP" |
| { |
| /* One of the ops has to be in a register or 0. */ |
| if (!register_operand (operand0, DImode) |
| && !register_operand (operand1, DImode)) |
| operands[1] = copy_to_mode_reg (DImode, operand1); |
| |
| if (register_operand (operand0, DImode) |
| && (CONST_INT_P (operands[1]) || CONST_DOUBLE_P (operands[1]))) |
| { |
| int i; |
| |
| for (i = 0; i < UNITS_PER_WORD * 2; i += UNITS_PER_WORD) |
| emit_move_insn (simplify_gen_subreg (SImode, operands[0], DImode, i), |
| simplify_gen_subreg (SImode, operands[1], DImode, i)); |
| DONE; |
| } |
| } |
| ) |
| |
| (define_insn "*movdi_internal" |
| [(set (match_operand:DI 0 "nonimmediate_operand" "=r,e!r,m") |
| (match_operand:DI 1 "nonimmediate_operand" "r,m,e!r"))] |
| "TARGET_V850E3V5_UP |
| || (register_operand (operands[0], DImode) && register_operand (operands[1], DImode))" |
| { return v850_gen_movdi (operands); } |
| [(set_attr "length" "4,12,12") |
| (set_attr "type" "other,load,store")]) |
| |
| ;; movqi |
| |
| (define_expand "movqi" |
| [(set (match_operand:QI 0 "general_operand" "") |
| (match_operand:QI 1 "general_operand" ""))] |
| "" |
| { |
| /* One of the ops has to be in a register or 0 */ |
| if (!register_operand (operand0, QImode) |
| && !reg_or_0_operand (operand1, QImode)) |
| operands[1] = copy_to_mode_reg (QImode, operand1); |
| }) |
| |
| (define_insn "*movqi_internal" |
| [(set (match_operand:QI 0 "nonimmediate_operand" "=r,r,r,Q,r,m,m") |
| (match_operand:QI 1 "general_operand" "Jr,n,Q,Ir,m,r,I"))] |
| "register_operand (operands[0], QImode) |
| || reg_or_0_operand (operands[1], QImode)" |
| { |
| return output_move_single (operands); |
| } |
| [(set_attr "length" "2,4,2,2,4,4,4") |
| (set_attr "type" "other,other,load,other,load,store,store")]) |
| |
| ;; movhi |
| |
| (define_expand "movhi" |
| [(set (match_operand:HI 0 "general_operand" "") |
| (match_operand:HI 1 "general_operand" ""))] |
| "" |
| { |
| /* One of the ops has to be in a register or 0 */ |
| if (!register_operand (operand0, HImode) |
| && !reg_or_0_operand (operand1, HImode)) |
| operands[1] = copy_to_mode_reg (HImode, operand1); |
| }) |
| |
| (define_insn "*movhi_internal" |
| [(set (match_operand:HI 0 "nonimmediate_operand" "=r,r,r,Q,r,m,m") |
| (match_operand:HI 1 "general_operand" "Jr,n,Q,Ir,m,r,I"))] |
| "register_operand (operands[0], HImode) |
| || reg_or_0_operand (operands[1], HImode)" |
| { |
| return output_move_single (operands); |
| } |
| [(set_attr "length" "2,4,2,2,4,4,4") |
| (set_attr "type" "other,other,load,other,load,store,store")]) |
| |
| ;; movsi and helpers |
| |
| (define_insn "*movsi_high" |
| [(set (match_operand:SI 0 "register_operand" "=r") |
| (high:SI (match_operand 1 "immediate_operand" "i")))] |
| "" |
| "movhi hi(%1),%.,%0" |
| [(set_attr "length" "4") |
| (set_attr "type" "other")]) |
| |
| (define_insn "*movsi_lo" |
| [(set (match_operand:SI 0 "register_operand" "=r") |
| (lo_sum:SI (match_operand:SI 1 "register_operand" "r") |
| (match_operand:SI 2 "immediate_operand" "i")))] |
| "" |
| "movea lo(%2),%1,%0" |
| [(set_attr "length" "4") |
| (set_attr "type" "other")]) |
| |
| (define_expand "movsi" |
| [(set (match_operand:SI 0 "general_operand" "") |
| (match_operand:SI 1 "general_operand" ""))] |
| "" |
| { |
| /* One of the ops has to be in a register or 0 */ |
| if (!register_operand (operand0, SImode) |
| && !reg_or_0_operand (operand1, SImode)) |
| operands[1] = copy_to_mode_reg (SImode, operand1); |
| |
| /* Some constants, as well as symbolic operands |
| must be done with HIGH & LO_SUM patterns. */ |
| if (CONSTANT_P (operands[1]) |
| && GET_CODE (operands[1]) != HIGH |
| && ! (TARGET_V850E_UP) |
| && !special_symbolref_operand (operands[1], VOIDmode) |
| && !(GET_CODE (operands[1]) == CONST_INT |
| && (CONST_OK_FOR_J (INTVAL (operands[1])) |
| || CONST_OK_FOR_K (INTVAL (operands[1])) |
| || CONST_OK_FOR_L (INTVAL (operands[1]))))) |
| { |
| rtx temp; |
| |
| if (reload_in_progress || reload_completed) |
| temp = operands[0]; |
| else |
| temp = gen_reg_rtx (SImode); |
| |
| emit_insn (gen_rtx_SET (temp, gen_rtx_HIGH (SImode, operand1))); |
| emit_insn (gen_rtx_SET (operand0, |
| gen_rtx_LO_SUM (SImode, temp, operand1))); |
| DONE; |
| } |
| }) |
| |
| ;; This is the same as the following pattern, except that it includes |
| ;; support for arbitrary 32-bit immediates. |
| |
| ;; ??? This always loads addresses using hilo. If the only use of this address |
| ;; was in a load/store, then we would get smaller code if we only loaded the |
| ;; upper part with hi, and then put the lower part in the load/store insn. |
| |
| (define_insn "*movsi_internal_v850e" |
| [(set (match_operand:SI 0 "nonimmediate_operand" "=r,r,r,r,Q,r,r,m,m,r") |
| (match_operand:SI 1 "general_operand" "Jr,K,L,Q,Ir,m,R,r,I,i"))] |
| "(TARGET_V850E_UP) |
| && (register_operand (operands[0], SImode) |
| || reg_or_0_operand (operands[1], SImode))" |
| { |
| return output_move_single (operands); |
| } |
| [(set_attr "length" "2,4,4,2,2,4,4,4,4,6") |
| (set_attr "type" "other,other,other,load,other,load,other,store,store,other")]) |
| |
| (define_insn "*movsi_internal" |
| [(set (match_operand:SI 0 "nonimmediate_operand" "=r,r,r,r,Q,r,r,m,m") |
| (match_operand:SI 1 "movsi_source_operand" "Jr,K,L,Q,Ir,m,R,r,I"))] |
| "register_operand (operands[0], SImode) |
| || reg_or_0_operand (operands[1], SImode)" |
| { |
| return output_move_single (operands); |
| } |
| [(set_attr "length" "2,4,4,2,2,4,4,4,4") |
| (set_attr "type" "other,other,other,load,other,load,store,store,other")]) |
| |
| (define_insn "*movsf_internal" |
| [(set (match_operand:SF 0 "nonimmediate_operand" "=r,r,r,r,r,Q,r,m,m,r") |
| (match_operand:SF 1 "general_operand" "Jr,K,L,n,Q,Ir,m,r,IG,iF"))] |
| "register_operand (operands[0], SFmode) |
| || reg_or_0_operand (operands[1], SFmode)" |
| { |
| return output_move_single (operands); |
| } |
| [(set_attr "length" "2,4,4,8,2,2,4,4,4,8") |
| (set_attr "type" "other,other,other,other,load,other,load,store,store,other")]) |
| |
| ;; ---------------------------------------------------------------------- |
| ;; TEST INSTRUCTIONS |
| ;; ---------------------------------------------------------------------- |
| |
| (define_insn "*v850_tst1" |
| [(set (reg:CCZ CC_REGNUM) |
| (compare (zero_extract:SI (match_operand:QI 0 "memory_operand" "m") |
| (const_int 1) |
| (match_operand:QI 1 "const_int_operand" "n")) |
| (const_int 0)))] |
| "reload_completed" |
| "tst1 %1,%0" |
| [(set_attr "length" "4")]) |
| |
| ;; This replaces ld.b;sar;andi with tst1;setf nz. |
| ;; Should there be variants for HI or SI modes? |
| |
| (define_insn_and_split "" |
| [(set (match_operand:SI 0 "register_operand" "") |
| (compare (zero_extract:SI (match_operand:QI 1 "memory_operand" "") |
| (const_int 1) |
| (match_operand 2 "const_int_operand" "")) |
| (const_int 0)))] |
| "" |
| "#" |
| "reload_completed" |
| [(set (reg:CCZ CC_REGNUM) (compare (zero_extract:SI (match_dup 1) |
| (const_int 1) |
| (match_dup 2)) |
| (const_int 0))) |
| (set (match_dup 0) (ne:SI (reg:CCZ CC_REGNUM) (const_int 0)))]) |
| |
| (define_expand "cbranchsi4" |
| [(set (pc) |
| (if_then_else |
| (match_operator 0 "comparison_operator" |
| [(match_operand:SI 1 "register_operand") |
| (match_operand:SI 2 "reg_or_int5_operand")]) |
| (label_ref (match_operand 3 "" "")) |
| (pc)))] |
| "") |
| |
| (define_insn "cmpsi_insn" |
| [(set (reg:CC CC_REGNUM) |
| (compare (match_operand:SI 0 "register_operand" "r,r") |
| (match_operand:SI 1 "reg_or_int5_operand" "r,J")))] |
| "reload_completed" |
| "cmp %1,%0" |
| [(set_attr "length" "2,2")]) |
| |
| (define_insn_and_split "cbranchsf4" |
| [(set (pc) |
| (if_then_else (match_operator 0 "ordered_comparison_operator" |
| [(match_operand:SF 1 "register_operand" "r") |
| (match_operand:SF 2 "register_operand" "r")]) |
| (label_ref (match_operand 3 "")) |
| (pc)))] |
| "TARGET_USE_FPU" |
| "#" |
| "reload_completed" |
| [(set (match_dup 4) (match_dup 5)) |
| (set (pc) |
| (if_then_else (match_dup 6) |
| (label_ref (match_dup 3)) |
| (pc)))] |
| "{ |
| /* This will generate the comparison insn at the start of |
| the sequence and get us the right mode to use for our |
| condition code registers. */ |
| enum machine_mode mode |
| = v850_gen_float_compare (GET_CODE (operands[0]), |
| GET_MODE (operands[1]), |
| operands[1], operands[2]); |
| /* We want operands referring to CC_REGNUM and FCC_REGNUM |
| in mode MODE. */ |
| operands[4] = gen_rtx_REG (mode, CC_REGNUM); |
| operands[5] = gen_rtx_REG (mode, FCC_REGNUM); |
| if (mode == CC_FPU_NEmode) |
| operands[6] = gen_rtx_NE (mode, operands[4], const0_rtx); |
| else |
| operands[6] = gen_rtx_EQ (mode, operands[4], const0_rtx); |
| }") |
| |
| (define_insn "cstoresf4" |
| [(set (match_operand:SI 0 "register_operand" "=r") |
| (match_operator:SI 1 "ordered_comparison_operator" |
| [(match_operand:SF 2 "register_operand" "r") |
| (match_operand:SF 3 "register_operand" "r")])) |
| (clobber (reg:CC CC_REGNUM))] |
| "TARGET_USE_FPU" |
| { |
| if (GET_CODE (operands[1]) == GT || GET_CODE (operands[1]) == GE) |
| return "cmpf.s %c1, %z2, %z3 ; trfsr ; setf nz, %0"; |
| if (GET_CODE (operands[1]) == LT || GET_CODE (operands[1]) == LE) |
| return "cmpf.s %c1, %z2, %z3 ; trfsr ; setf z, %0"; |
| if (GET_CODE (operands[1]) == EQ) |
| return "cmpf.s eq, %z2, %z3 ; trfsr ; setf z, %0"; |
| if (GET_CODE (operands[1]) == NE) |
| return "cmpf.s neq, %z2, %z3 ; trfsr ; setf nz, %0"; |
| gcc_unreachable (); |
| } |
| [(set_attr "length" "12") |
| (set_attr "type" "fpu")]) |
| |
| (define_insn_and_split "cbranchdf4" |
| [(set (pc) |
| (if_then_else (match_operator 0 "ordered_comparison_operator" |
| [(match_operand:DF 1 "even_reg_operand" "r") |
| (match_operand:DF 2 "even_reg_operand" "r")]) |
| (label_ref (match_operand 3 "")) |
| (pc)))] |
| "TARGET_USE_FPU" |
| "#" |
| "reload_completed" |
| ;; How to get the mode here? |
| [(set (match_dup 4) (match_dup 5)) |
| (set (pc) |
| (if_then_else (match_dup 6) |
| (label_ref (match_dup 3)) |
| (pc)))] |
| "{ |
| /* This will generate the comparison insn at the start of |
| the sequence and get us the right mode to use for our |
| condition code registers. */ |
| enum machine_mode mode |
| = v850_gen_float_compare (GET_CODE (operands[0]), |
| GET_MODE (operands[1]), |
| operands[1], operands[2]); |
| PUT_MODE (operands[0], mode); |
| /* We want operands referring to CC_REGNUM and FCC_REGNUM |
| in mode MODE. */ |
| operands[4] = gen_rtx_REG (mode, CC_REGNUM); |
| operands[5] = gen_rtx_REG (mode, FCC_REGNUM); |
| if (mode == CC_FPU_NEmode) |
| operands[6] = gen_rtx_NE (mode, operands[4], const0_rtx); |
| else |
| operands[6] = gen_rtx_EQ (mode, operands[4], const0_rtx); |
| }") |
| |
| (define_insn "cstoredf4" |
| [(set (match_operand:SI 0 "register_operand" "=r") |
| (match_operator:SI 1 "ordered_comparison_operator" |
| [(match_operand:DF 2 "even_reg_operand" "r") |
| (match_operand:DF 3 "even_reg_operand" "r")])) |
| (clobber (reg:CC CC_REGNUM))] |
| "TARGET_USE_FPU" |
| { |
| if (GET_CODE (operands[1]) == GT || GET_CODE (operands[1]) == GE) |
| return "cmpf.d %c1, %z2, %z3 ; trfsr ; setf nz, %0"; |
| if (GET_CODE (operands[1]) == LT || GET_CODE (operands[1]) == LE) |
| return "cmpf.d %c1, %z2, %z3 ; trfsr ; setf z, %0"; |
| if (GET_CODE (operands[1]) == EQ) |
| return "cmpf.d eq, %z2, %z3 ; trfsr ; setf z ,%0"; |
| if (GET_CODE (operands[1]) == NE) |
| return "cmpf.d neq, %z2, %z3 ; trfsr ; setf nz, %0"; |
| gcc_unreachable (); |
| } |
| [(set_attr "length" "12") |
| (set_attr "type" "fpu")]) |
| |
| ;; ---------------------------------------------------------------------- |
| ;; ADD INSTRUCTIONS |
| ;; ---------------------------------------------------------------------- |
| |
| (define_insn_and_split "addsi3" |
| [(set (match_operand:SI 0 "register_operand" "=r,r,r") |
| (plus:SI (match_operand:SI 1 "register_operand" "%0,r,r") |
| (match_operand:SI 2 "nonmemory_operand" "rJ,K,U")))] |
| "" |
| "#" |
| "reload_completed" |
| [(parallel [(set (match_dup 0) (plus:SI (match_dup 1) (match_dup 2))) |
| (clobber (reg:CC CC_REGNUM))])]) |
| |
| (define_insn "addsi3_clobber_flags" |
| [(set (match_operand:SI 0 "register_operand" "=r,r,r") |
| (plus:SI (match_operand:SI 1 "register_operand" "%0,r,r") |
| (match_operand:SI 2 "nonmemory_operand" "rJ,K,U"))) |
| (clobber (reg:CC CC_REGNUM))] |
| "" |
| "@ |
| add %2,%0 |
| addi %2,%1,%0 |
| addi %O2(%P2),%1,%0" |
| [(set_attr "length" "2,4,4")]) |
| |
| (define_insn "addsi3_set_flags" |
| [(set (reg:CCNZ CC_REGNUM) |
| (compare:CCNZ (plus:SI (match_operand:SI 1 "register_operand" "%0,r,r") |
| (match_operand:SI 2 "nonmemory_operand" "rJ,K,U")) |
| (const_int 0))) |
| (set (match_operand:SI 0 "register_operand" "=r,r,r") |
| (plus:SI (match_dup 1) (match_dup 2)))] |
| "reload_completed" |
| "@ |
| add %2,%0 |
| addi %2,%1,%0 |
| addi %O2(%P2),%1,%0" |
| [(set_attr "length" "2,4,4")]) |
| |
| ;; ---------------------------------------------------------------------- |
| ;; SUBTRACT INSTRUCTIONS |
| ;; ---------------------------------------------------------------------- |
| (define_insn_and_split "subsi3" |
| [(set (match_operand:SI 0 "register_operand" "=r,r") |
| (minus:SI (match_operand:SI 1 "register_operand" "0,r") |
| (match_operand:SI 2 "register_operand" "r,0")))] |
| "" |
| "#" |
| "reload_completed" |
| [(parallel [(set (match_dup 0) (minus:SI (match_dup 1) (match_dup 2))) |
| (clobber (reg:CC CC_REGNUM))])]) |
| |
| (define_insn "subsi3_clobber_flags" |
| [(set (match_operand:SI 0 "register_operand" "=r,r") |
| (minus:SI (match_operand:SI 1 "register_operand" "0,r") |
| (match_operand:SI 2 "register_operand" "r,0"))) |
| (clobber (reg:CC CC_REGNUM))] |
| "" |
| "@ |
| sub %2,%0 |
| subr %1,%0" |
| [(set_attr "length" "2,2")]) |
| |
| (define_insn "*subsi3_set_flags" |
| [(set (reg:CCNZ CC_REGNUM) |
| (compare:CCNZ (minus:SI (match_operand:SI 1 "register_operand" "0,r") |
| (match_operand:SI 2 "nonmemory_operand" "r,0")) |
| (const_int 0))) |
| (set (match_operand:SI 0 "register_operand" "=r,r") |
| (minus:SI (match_dup 1) (match_dup 2)))] |
| "reload_completed" |
| "@ |
| sub %2,%0 |
| subr %1,%0" |
| [(set_attr "length" "2,2")]) |
| |
| (define_insn_and_split "negsi2" |
| [(set (match_operand:SI 0 "register_operand" "=r") |
| (neg:SI (match_operand:SI 1 "register_operand" "0")))] |
| "" |
| "#" |
| "reload_completed" |
| [(parallel [(set (match_dup 0) (neg:SI (match_dup 1))) |
| (clobber (reg:CC CC_REGNUM))])]) |
| |
| (define_insn "negsi2_clobber_flags" |
| [(set (match_operand:SI 0 "register_operand" "=r") |
| (neg:SI (match_operand:SI 1 "register_operand" "0"))) |
| (clobber (reg:CC CC_REGNUM))] |
| "" |
| "subr %.,%0" |
| [(set_attr "length" "2")]) |
| |
| (define_insn "*negsi2_set_flags" |
| [(set (reg:CCNZ CC_REGNUM) |
| (compare:CCNZ (neg:SI (match_operand:SI 1 "register_operand" "0")) |
| (const_int 0))) |
| (set (match_operand:SI 0 "register_operand" "=r") |
| (neg:SI (match_dup 1)))] |
| "reload_completed" |
| "subr %.,%0" |
| [(set_attr "length" "2")]) |
| |
| ;; ---------------------------------------------------------------------- |
| ;; MULTIPLY INSTRUCTIONS |
| ;; ---------------------------------------------------------------------- |
| |
| (define_expand "mulhisi3" |
| [(set (match_operand:SI 0 "register_operand" "") |
| (mult:SI |
| (sign_extend:SI (match_operand:HI 1 "register_operand" "")) |
| (sign_extend:SI (match_operand:HI 2 "nonmemory_operand" ""))))] |
| "" |
| { |
| if (GET_CODE (operands[2]) == CONST_INT) |
| { |
| emit_insn (gen_mulhisi3_internal2 (operands[0], operands[1], operands[2])); |
| DONE; |
| } |
| }) |
| |
| (define_insn "*mulhisi3_internal1" |
| [(set (match_operand:SI 0 "register_operand" "=r") |
| (mult:SI |
| (sign_extend:SI (match_operand:HI 1 "register_operand" "%0")) |
| (sign_extend:SI (match_operand:HI 2 "register_operand" "r"))))] |
| "" |
| "mulh %2,%0" |
| [(set_attr "length" "2") |
| (set_attr "type" "mult")]) |
| |
| (define_insn "mulhisi3_internal2" |
| [(set (match_operand:SI 0 "register_operand" "=r,r") |
| (mult:SI |
| (sign_extend:SI (match_operand:HI 1 "register_operand" "%0,r")) |
| (match_operand:HI 2 "const_int_operand" "J,K")))] |
| "" |
| "@ |
| mulh %2,%0 |
| mulhi %2,%1,%0" |
| [(set_attr "length" "2,4") |
| (set_attr "type" "mult")]) |
| |
| ;; ??? The scheduling info is probably wrong. |
| |
| ;; ??? This instruction can also generate the 32-bit highpart, but using it |
| ;; may increase code size counter to the desired result. |
| |
| ;; ??? This instructions can also give a DImode result. |
| |
| ;; ??? There is unsigned version, but it matters only for the DImode/highpart |
| ;; results. |
| |
| (define_insn "mulsi3" |
| [(set (match_operand:SI 0 "register_operand" "=r") |
| (mult:SI (match_operand:SI 1 "register_operand" "%0") |
| (match_operand:SI 2 "reg_or_int9_operand" "rO")))] |
| "(TARGET_V850E_UP)" |
| "mul %2,%1,%." |
| [(set_attr "length" "4") |
| (set_attr "type" "mult")]) |
| |
| ;; ---------------------------------------------------------------------- |
| ;; DIVIDE INSTRUCTIONS |
| ;; ---------------------------------------------------------------------- |
| |
| ;; ??? These insns do set the Z/N condition codes, except that they are based |
| ;; on only one of the two results, so it doesn't seem to make sense to use |
| ;; them. |
| |
| ;; ??? The scheduling info is probably wrong. |
| |
| (define_insn "divmodsi4" |
| [(set (match_operand:SI 0 "register_operand" "=r") |
| (div:SI (match_operand:SI 1 "register_operand" "0") |
| (match_operand:SI 2 "register_operand" "r"))) |
| (set (match_operand:SI 3 "register_operand" "=r") |
| (mod:SI (match_dup 1) |
| (match_dup 2))) |
| (clobber (reg:CC CC_REGNUM))] |
| "TARGET_V850E_UP" |
| { |
| if (TARGET_V850E2_UP) |
| return "divq %2,%0,%3"; |
| else |
| return "div %2,%0,%3"; |
| } |
| [(set_attr "length" "4") |
| (set_attr "type" "div")]) |
| |
| (define_insn "udivmodsi4" |
| [(set (match_operand:SI 0 "register_operand" "=r") |
| (udiv:SI (match_operand:SI 1 "register_operand" "0") |
| (match_operand:SI 2 "register_operand" "r"))) |
| (set (match_operand:SI 3 "register_operand" "=r") |
| (umod:SI (match_dup 1) |
| (match_dup 2))) |
| (clobber (reg:CC CC_REGNUM))] |
| "TARGET_V850E_UP" |
| { |
| if (TARGET_V850E2_UP) |
| return "divqu %2,%0,%3"; |
| else |
| return "divu %2,%0,%3"; |
| } |
| [(set_attr "length" "4") |
| (set_attr "type" "div")]) |
| |
| ;; ??? There is a 2 byte instruction for generating only the quotient. |
| ;; However, it isn't clear how to compute the length field correctly. |
| |
| (define_insn "divmodhi4" |
| [(set (match_operand:HI 0 "register_operand" "=r") |
| (div:HI (match_operand:HI 1 "register_operand" "0") |
| (match_operand:HI 2 "register_operand" "r"))) |
| (set (match_operand:HI 3 "register_operand" "=r") |
| (mod:HI (match_dup 1) |
| (match_dup 2))) |
| (clobber (reg:CC CC_REGNUM))] |
| "TARGET_V850E_UP" |
| "sxh %0\\n\\tdivh %2,%0,%3" |
| [(set_attr "length" "6") |
| (set_attr "type" "div")]) |
| |
| ;; The half word needs to be zero/sign extended to 32 bits before doing |
| ;; the division/modulo operation. |
| |
| (define_insn "udivmodhi4" |
| [(set (match_operand:HI 0 "register_operand" "=r") |
| (udiv:HI (match_operand:HI 1 "register_operand" "0") |
| (match_operand:HI 2 "register_operand" "r"))) |
| (set (match_operand:HI 3 "register_operand" "=r") |
| (umod:HI (match_dup 1) |
| (match_dup 2))) |
| (clobber (reg:CC CC_REGNUM))] |
| "TARGET_V850E_UP" |
| "zxh %0\\n\\ndivhu %2,%0,%3" |
| [(set_attr "length" "6") |
| (set_attr "type" "div")]) |
| |
| ;; ---------------------------------------------------------------------- |
| ;; AND INSTRUCTIONS |
| ;; ---------------------------------------------------------------------- |
| |
| (define_insn "*v850_clr1_1" |
| [(set (match_operand:QI 0 "memory_operand" "=m") |
| (subreg:QI |
| (and:SI (subreg:SI (match_dup 0) 0) |
| (match_operand:QI 1 "not_power_of_two_operand" "")) 0)) |
| (clobber (reg:CC CC_REGNUM))] |
| "" |
| { |
| rtx xoperands[2]; |
| xoperands[0] = operands[0]; |
| xoperands[1] = GEN_INT (~INTVAL (operands[1]) & 0xff); |
| output_asm_insn ("clr1 %M1,%0", xoperands); |
| return ""; |
| } |
| [(set_attr "length" "4") |
| (set_attr "type" "bit1")]) |
| |
| (define_insn "*v850_clr1_2" |
| [(set (match_operand:HI 0 "indirect_operand" "=m") |
| (subreg:HI |
| (and:SI (subreg:SI (match_dup 0) 0) |
| (match_operand:HI 1 "not_power_of_two_operand" "")) 0)) |
| (clobber (reg:CC CC_REGNUM))] |
| "" |
| { |
| int log2 = exact_log2 (~INTVAL (operands[1]) & 0xffff); |
| |
| rtx xoperands[2]; |
| xoperands[0] = gen_rtx_MEM (QImode, |
| plus_constant (Pmode, XEXP (operands[0], 0), |
| log2 / 8)); |
| xoperands[1] = GEN_INT (log2 % 8); |
| output_asm_insn ("clr1 %1,%0", xoperands); |
| return ""; |
| } |
| [(set_attr "length" "4") |
| (set_attr "type" "bit1")]) |
| |
| (define_insn "*v850_clr1_3" |
| [(set (match_operand:SI 0 "indirect_operand" "=m") |
| (and:SI (match_dup 0) |
| (match_operand:SI 1 "not_power_of_two_operand" ""))) |
| (clobber (reg:CC CC_REGNUM))] |
| "" |
| { |
| int log2 = exact_log2 (~INTVAL (operands[1]) & 0xffffffff); |
| |
| rtx xoperands[2]; |
| xoperands[0] = gen_rtx_MEM (QImode, |
| plus_constant (Pmode, XEXP (operands[0], 0), |
| log2 / 8)); |
| xoperands[1] = GEN_INT (log2 % 8); |
| output_asm_insn ("clr1 %1,%0", xoperands); |
| return ""; |
| } |
| [(set_attr "length" "4") |
| (set_attr "type" "bit1")]) |
| |
| (define_insn_and_split "andsi3" |
| [(set (match_operand:SI 0 "register_operand" "=r,r,r") |
| (and:SI (match_operand:SI 1 "register_operand" "%0,0,r") |
| (match_operand:SI 2 "nonmemory_operand" "r,I,M")))] |
| "" |
| "#" |
| "reload_completed" |
| [(parallel [(set (match_dup 0) (and:SI (match_dup 1) (match_dup 2))) |
| (clobber (reg:CC CC_REGNUM))])]) |
| |
| (define_insn "andsi3_clobber_flags" |
| [(set (match_operand:SI 0 "register_operand" "=r,r,r") |
| (and:SI (match_operand:SI 1 "register_operand" "%0,0,r") |
| (match_operand:SI 2 "nonmemory_operand" "r,I,M"))) |
| (clobber (reg:CC CC_REGNUM))] |
| "reload_completed" |
| "@ |
| and %2,%0 |
| and %.,%0 |
| andi %2,%1,%0" |
| [(set_attr "length" "2,2,4")]) |
| |
| (define_insn "andsi3_set_flags" |
| [(set (reg:CC CC_REGNUM) |
| (compare:CC (and:SI (match_operand:SI 1 "register_operand" "%0,0,r") |
| (match_operand:SI 2 "nonmemory_operand" "r,I,M")) |
| (const_int 0))) |
| (set (match_operand:SI 0 "register_operand" "=r,r,r") |
| (and:SI (match_dup 1) (match_dup 2)))] |
| "reload_completed" |
| "@ |
| and %2,%0 |
| and %.,%0 |
| andi %2,%1,%0" |
| [(set_attr "length" "2,2,4")]) |
| |
| ;; ---------------------------------------------------------------------- |
| ;; OR INSTRUCTIONS |
| ;; ---------------------------------------------------------------------- |
| |
| (define_insn "*v850_set1_1" |
| [(set (match_operand:QI 0 "memory_operand" "=m") |
| (subreg:QI (ior:SI (subreg:SI (match_dup 0) 0) |
| (match_operand 1 "power_of_two_operand" "")) 0)) |
| (clobber (reg:CC CC_REGNUM))] |
| "" |
| "set1 %M1,%0" |
| [(set_attr "length" "4") |
| (set_attr "type" "bit1")]) |
| |
| (define_insn "*v850_set1_2" |
| [(set (match_operand:HI 0 "indirect_operand" "=m") |
| (subreg:HI (ior:SI (subreg:SI (match_dup 0) 0) |
| (match_operand 1 "power_of_two_operand" "")) 0))] |
| "" |
| { |
| int log2 = exact_log2 (INTVAL (operands[1])); |
| |
| if (log2 < 8) |
| return "set1 %M1,%0"; |
| else |
| { |
| rtx xoperands[2]; |
| xoperands[0] = gen_rtx_MEM (QImode, |
| plus_constant (Pmode, XEXP (operands[0], 0), |
| log2 / 8)); |
| xoperands[1] = GEN_INT (log2 % 8); |
| output_asm_insn ("set1 %1,%0", xoperands); |
| } |
| return ""; |
| } |
| [(set_attr "length" "4") |
| (set_attr "type" "bit1")]) |
| |
| (define_insn "*v850_set1_3" |
| [(set (match_operand:SI 0 "indirect_operand" "=m") |
| (ior:SI (match_dup 0) |
| (match_operand 1 "power_of_two_operand" ""))) |
| (clobber (reg:CC CC_REGNUM))] |
| "" |
| { |
| int log2 = exact_log2 (INTVAL (operands[1])); |
| |
| if (log2 < 8) |
| return "set1 %M1,%0"; |
| else |
| { |
| rtx xoperands[2]; |
| xoperands[0] = gen_rtx_MEM (QImode, |
| plus_constant (Pmode, XEXP (operands[0], 0), |
| log2 / 8)); |
| xoperands[1] = GEN_INT (log2 % 8); |
| output_asm_insn ("set1 %1,%0", xoperands); |
| } |
| return ""; |
| } |
| [(set_attr "length" "4") |
| (set_attr "type" "bit1")]) |
| |
| (define_insn_and_split "iorsi3" |
| [(set (match_operand:SI 0 "register_operand" "=r,r,r") |
| (ior:SI (match_operand:SI 1 "register_operand" "%0,0,r") |
| (match_operand:SI 2 "nonmemory_operand" "r,I,M")))] |
| "" |
| "#" |
| "reload_completed" |
| [(parallel [(set (match_dup 0) (ior:SI (match_dup 1) (match_dup 2))) |
| (clobber (reg:CC CC_REGNUM))])]) |
| |
| (define_insn "iorsi3_clobber_flags" |
| [(set (match_operand:SI 0 "register_operand" "=r,r,r") |
| (ior:SI (match_operand:SI 1 "register_operand" "%0,0,r") |
| (match_operand:SI 2 "nonmemory_operand" "r,I,M"))) |
| (clobber (reg:CC CC_REGNUM))] |
| "" |
| "@ |
| or %2,%0 |
| or %.,%0 |
| ori %2,%1,%0" |
| [(set_attr "length" "2,2,4")]) |
| |
| (define_insn "*iorsi3_set_flags" |
| [(set (reg:CC CC_REGNUM) |
| (compare:CC (ior:SI (match_operand:SI 1 "register_operand" "%0,0,r") |
| (match_operand:SI 2 "nonmemory_operand" "r,I,M")) |
| (const_int 0))) |
| (set (match_operand:SI 0 "register_operand" "=r,r,r") |
| (ior:SI (match_dup 1) (match_dup 2)))] |
| "reload_completed" |
| "@ |
| or %2,%0 |
| or %.,%0 |
| ori %2,%1,%0" |
| [(set_attr "length" "2,2,4")]) |
| |
| ;; ---------------------------------------------------------------------- |
| ;; XOR INSTRUCTIONS |
| ;; ---------------------------------------------------------------------- |
| |
| (define_insn "*v850_not1_1" |
| [(set (match_operand:QI 0 "memory_operand" "=m") |
| (subreg:QI (xor:SI (subreg:SI (match_dup 0) 0) |
| (match_operand 1 "power_of_two_operand" "")) 0)) |
| (clobber (reg:CC CC_REGNUM))] |
| "" |
| "not1 %M1,%0" |
| [(set_attr "length" "4") |
| (set_attr "type" "bit1")]) |
| |
| (define_insn "*v850_not1_2" |
| [(set (match_operand:HI 0 "indirect_operand" "=m") |
| (subreg:HI (xor:SI (subreg:SI (match_dup 0) 0) |
| (match_operand 1 "power_of_two_operand" "")) 0))] |
| "" |
| { |
| int log2 = exact_log2 (INTVAL (operands[1])); |
| |
| if (log2 < 8) |
| return "not1 %M1,%0"; |
| else |
| { |
| rtx xoperands[2]; |
| xoperands[0] = gen_rtx_MEM (QImode, |
| plus_constant (Pmode, XEXP (operands[0], 0), |
| log2 / 8)); |
| xoperands[1] = GEN_INT (log2 % 8); |
| output_asm_insn ("not1 %1,%0", xoperands); |
| } |
| return ""; |
| } |
| [(set_attr "length" "4") |
| (set_attr "type" "bit1")]) |
| |
| (define_insn "*v850_not1_3" |
| [(set (match_operand:SI 0 "indirect_operand" "=m") |
| (xor:SI (match_dup 0) |
| (match_operand 1 "power_of_two_operand" ""))) |
| (clobber (reg:CC CC_REGNUM))] |
| "" |
| { |
| int log2 = exact_log2 (INTVAL (operands[1])); |
| |
| if (log2 < 8) |
| return "not1 %M1,%0"; |
| else |
| { |
| rtx xoperands[2]; |
| xoperands[0] = gen_rtx_MEM (QImode, |
| plus_constant (Pmode, XEXP (operands[0], 0), |
| log2 / 8)); |
| xoperands[1] = GEN_INT (log2 % 8); |
| output_asm_insn ("not1 %1,%0", xoperands); |
| } |
| return ""; |
| } |
| [(set_attr "length" "4") |
| (set_attr "type" "bit1")]) |
| |
| (define_insn_and_split "xorsi3" |
| [(set (match_operand:SI 0 "register_operand" "=r,r,r") |
| (xor:SI (match_operand:SI 1 "register_operand" "%0,0,r") |
| (match_operand:SI 2 "nonmemory_operand" "r,I,M")))] |
| "" |
| "#" |
| "reload_completed" |
| [(parallel [(set (match_dup 0) (xor:SI (match_dup 1) (match_dup 2))) |
| (clobber (reg:CC CC_REGNUM))])]) |
| |
| (define_insn "xorsi3_clobber_flags" |
| [(set (match_operand:SI 0 "register_operand" "=r,r,r") |
| (xor:SI (match_operand:SI 1 "register_operand" "%0,0,r") |
| (match_operand:SI 2 "nonmemory_operand" "r,I,M"))) |
| (clobber (reg:CC CC_REGNUM))] |
| "" |
| "@ |
| xor %2,%0 |
| xor %.,%0 |
| xori %2,%1,%0" |
| [(set_attr "length" "2,2,4")]) |
| |
| (define_insn "*xorsi3_set_flags" |
| [(set (reg:CC CC_REGNUM) |
| (compare:CC (xor:SI (match_operand:SI 1 "register_operand" "%0,0,r") |
| (match_operand:SI 2 "nonmemory_operand" "r,I,M")) |
| (const_int 0))) |
| (set (match_operand:SI 0 "register_operand" "=r,r,r") |
| (xor:SI (match_dup 1) (match_dup 2)))] |
| "reload_completed" |
| "@ |
| xor %2,%0 |
| xor %.,%0 |
| xori %2,%1,%0" |
| [(set_attr "length" "2,2,4")]) |
| |
| |
| ;; ---------------------------------------------------------------------- |
| ;; NOT INSTRUCTIONS |
| ;; ---------------------------------------------------------------------- |
| |
| (define_insn_and_split "one_cmplsi2" |
| [(set (match_operand:SI 0 "register_operand" "=r") |
| (not:SI (match_operand:SI 1 "register_operand" "r")))] |
| "" |
| "#" |
| "reload_completed" |
| [(parallel [(set (match_dup 0) (not:SI (match_dup 1))) |
| (clobber (reg:CC CC_REGNUM))])]) |
| |
| |
| (define_insn "one_cmplsi2_clobber_flags" |
| [(set (match_operand:SI 0 "register_operand" "=r") |
| (not:SI (match_operand:SI 1 "register_operand" "r"))) |
| (clobber (reg:CC CC_REGNUM))] |
| "" |
| "not %1,%0" |
| [(set_attr "length" "2")]) |
| |
| (define_insn "*one_cmplsi2_set_flags" |
| [(set (reg:CC CC_REGNUM) |
| (compare:CC (not:SI (match_operand:SI 1 "register_operand" "r")) |
| (const_int 0))) |
| (set (match_operand:SI 0 "register_operand" "=r") |
| (not:SI (match_dup 1)))] |
| "reload_completed" |
| "not %1,%0" |
| [(set_attr "length" "2")]) |
| |
| ;; ----------------------------------------------------------------- |
| ;; BIT FIELDS |
| ;; ----------------------------------------------------------------- |
| |
| ;; ??? Is it worth defining insv and extv for the V850 series?!? |
| |
| ;; An insv pattern would be useful, but does not get used because |
| ;; store_bit_field never calls insv when storing a constant value into a |
| ;; single-bit bitfield. |
| |
| ;; extv/extzv patterns would be useful, but do not get used because |
| ;; optimize_bitfield_compare in fold-const usually converts single |
| ;; bit extracts into an AND with a mask. |
| |
| (define_insn "insv" |
| [(set (zero_extract:SI (match_operand:SI 0 "register_operand" "+r") |
| (match_operand:SI 1 "immediate_operand" "n") |
| (match_operand:SI 2 "immediate_operand" "n")) |
| (match_operand:SI 3 "register_operand" "r")) |
| (clobber (reg:CC CC_REGNUM))] |
| "TARGET_V850E3V5_UP" |
| "bins %3, %2, %1, %0" |
| [(set_attr "length" "4")]) |
| |
| ;; ----------------------------------------------------------------- |
| ;; Scc INSTRUCTIONS |
| ;; ----------------------------------------------------------------- |
| |
| (define_insn_and_split "*cbranchsi4_insn" |
| [(set (pc) |
| (if_then_else (match_operator 0 "comparison_operator" |
| [(match_operand:SI 1 "register_operand" "r") |
| (match_operand:SI 2 "reg_or_int5_operand" "rJ")]) |
| (label_ref (match_operand 3 "" "")) |
| (pc)))] |
| "" |
| "#" |
| "reload_completed" |
| [(set (reg:CC CC_REGNUM) |
| (compare:CC (match_dup 1) (match_dup 2))) |
| (set (pc) |
| (if_then_else (match_op_dup 0 |
| [(reg:CC CC_REGNUM) (const_int 0)]) |
| (label_ref (match_dup 3)) |
| (pc)))] |
| "") |
| |
| |
| (define_insn_and_split "cstoresi4" |
| [(set (match_operand:SI 0 "register_operand" "=r") |
| (match_operator:SI 1 "comparison_operator" |
| [(match_operand:SI 2 "register_operand" "r") |
| (match_operand:SI 3 "reg_or_int5_operand" "rJ")]))] |
| "" |
| "#" |
| "reload_completed" |
| [(set (reg:CC CC_REGNUM) |
| (compare:CC (match_dup 2) (match_dup 3))) |
| (set (match_dup 0) (match_op_dup 1 |
| [(reg:CC CC_REGNUM) (const_int 0)]))] |
| "") |
| |
| (define_insn "*setcc_insn" |
| [(set (match_operand:SI 0 "register_operand" "=r") |
| (match_operator:SI 1 "comparison_operator" |
| [(reg:CC CC_REGNUM) (const_int 0)]))] |
| "reload_completed" |
| "setf %c1,%0" |
| [(set_attr "length" "4")]) |
| |
| (define_insn "set_z_insn" |
| [(set (match_operand:SI 0 "register_operand" "=r") |
| (match_operand 1 "v850_float_z_comparison_operator" ""))] |
| "TARGET_V850E2V3_UP" |
| "setf z,%0" |
| [(set_attr "length" "4")]) |
| |
| (define_insn "set_nz_insn" |
| [(set (match_operand:SI 0 "register_operand" "=r") |
| (match_operand 1 "v850_float_nz_comparison_operator" ""))] |
| "TARGET_V850E2V3_UP" |
| "setf nz,%0" |
| [(set_attr "length" "4")]) |
| |
| ;; ---------------------------------------------------------------------- |
| ;; CONDITIONAL MOVE INSTRUCTIONS |
| ;; ---------------------------------------------------------------------- |
| |
| ;; Instructions using cc0 aren't allowed to have input reloads, so we must |
| ;; hide the fact that this instruction uses cc0. We do so by including the |
| ;; compare instruction inside it. |
| |
| (define_expand "movsicc" |
| [(set (match_operand:SI 0 "register_operand" "=r") |
| (if_then_else:SI |
| (match_operand 1 "comparison_operator") |
| (match_operand:SI 2 "reg_or_const_operand" "rJ") |
| (match_operand:SI 3 "reg_or_const_operand" "rI")))] |
| "(TARGET_V850E_UP)" |
| { |
| /* Make sure that we have an integer comparison... */ |
| if (GET_MODE (XEXP (operands[1], 0)) != CCmode |
| && GET_MODE (XEXP (operands[1], 0)) != SImode) |
| FAIL; |
| |
| if ((GET_CODE (operands[2]) == CONST_INT |
| && GET_CODE (operands[3]) == CONST_INT)) |
| { |
| int o2 = INTVAL (operands[2]); |
| int o3 = INTVAL (operands[3]); |
| |
| if (o2 == 1 && o3 == 0) |
| FAIL; /* setf */ |
| if (o3 == 1 && o2 == 0) |
| FAIL; /* setf */ |
| if (o2 == 0 && (o3 < -16 || o3 > 15) && exact_log2 (o3) >= 0) |
| FAIL; /* setf + shift */ |
| if (o3 == 0 && (o2 < -16 || o2 > 15) && exact_log2 (o2) >=0) |
| FAIL; /* setf + shift */ |
| if (o2 != 0) |
| operands[2] = copy_to_mode_reg (SImode, operands[2]); |
| if (o3 !=0 ) |
| operands[3] = copy_to_mode_reg (SImode, operands[3]); |
| } |
| else |
| { |
| if (GET_CODE (operands[2]) != REG) |
| operands[2] = copy_to_mode_reg (SImode,operands[2]); |
| if (GET_CODE (operands[3]) != REG) |
| operands[3] = copy_to_mode_reg (SImode, operands[3]); |
| } |
| }) |
| |
| (define_insn "movsicc_normal_cc" |
| [(set (match_operand:SI 0 "register_operand" "=r") |
| (if_then_else:SI |
| (match_operator 1 "comparison_operator" |
| [(reg:CC CC_REGNUM) (const_int 0)]) |
| (match_operand:SI 2 "reg_or_int5_operand" "rJ") |
| (match_operand:SI 3 "reg_or_0_operand" "rI")))] |
| "reload_completed && (TARGET_V850E_UP)" |
| "cmov %c1,%2,%z3,%0"; |
| [(set_attr "length" "6")]) |
| |
| (define_insn "movsicc_reversed_cc" |
| [(set (match_operand:SI 0 "register_operand" "=r") |
| (if_then_else:SI |
| (match_operator 1 "comparison_operator" |
| [(reg:CC CC_REGNUM) (const_int 0)]) |
| (match_operand:SI 2 "reg_or_0_operand" "rI") |
| (match_operand:SI 3 "reg_or_int5_operand" "rJ")))] |
| "reload_completed && (TARGET_V850E_UP)" |
| "cmov %C1,%3,%z2,%0" |
| [(set_attr "length" "6")]) |
| |
| (define_insn_and_split "*movsicc_normal" |
| [(set (match_operand:SI 0 "register_operand" "=r") |
| (if_then_else:SI |
| (match_operator 1 "comparison_operator" |
| [(match_operand:SI 4 "register_operand" "r") |
| (match_operand:SI 5 "reg_or_int5_operand" "rJ")]) |
| (match_operand:SI 2 "reg_or_int5_operand" "rJ") |
| (match_operand:SI 3 "reg_or_0_operand" "rI")))] |
| "(TARGET_V850E_UP)" |
| "#" |
| "reload_completed" |
| [(set (reg:CC CC_REGNUM) |
| (compare:CC (match_dup 4) (match_dup 5))) |
| (set (match_dup 0) |
| (if_then_else:SI (match_op_dup 1 |
| [(reg:CC CC_REGNUM) (const_int 0)]) |
| (match_dup 2) (match_dup 3)))]) |
| |
| |
| (define_insn_and_split "*movsicc_reversed" |
| [(set (match_operand:SI 0 "register_operand" "=r") |
| (if_then_else:SI |
| (match_operator 1 "comparison_operator" |
| [(match_operand:SI 4 "register_operand" "r") |
| (match_operand:SI 5 "reg_or_int5_operand" "rJ")]) |
| (match_operand:SI 2 "reg_or_int5_operand" "rI") |
| (match_operand:SI 3 "reg_or_0_operand" "rJ")))] |
| "(TARGET_V850E_UP)" |
| "#" |
| "reload_completed" |
| [(set (reg:CC CC_REGNUM) |
| (compare:CC (match_dup 4) (match_dup 5))) |
| (set (match_dup 0) |
| (if_then_else:SI (match_op_dup 1 |
| [(reg:CC CC_REGNUM) (const_int 0)]) |
| (match_dup 2) (match_dup 3)))]) |
| |
| ;; We could expose the setting of the condition codes here. |
| (define_insn "*movsicc_tst1" |
| [(set (match_operand:SI 0 "register_operand" "=r") |
| (if_then_else:SI |
| (match_operator 1 "comparison_operator" |
| [(zero_extract:SI |
| (match_operand:QI 2 "memory_operand" "m") |
| (const_int 1) |
| (match_operand 3 "const_int_operand" "n")) |
| (const_int 0)]) |
| (match_operand:SI 4 "reg_or_int5_operand" "rJ") |
| (match_operand:SI 5 "reg_or_0_operand" "rI"))) |
| (clobber (reg:CC CC_REGNUM))] |
| "(TARGET_V850E_UP)" |
| "tst1 %3,%2 ; cmov %c1,%4,%z5,%0" |
| [(set_attr "length" "8")]) |
| |
| ;; We could expose the setting of the condition codes here. |
| (define_insn "*movsicc_tst1_reversed" |
| [(set (match_operand:SI 0 "register_operand" "=r") |
| (if_then_else:SI |
| (match_operator 1 "comparison_operator" |
| [(zero_extract:SI |
| (match_operand:QI 2 "memory_operand" "m") |
| (const_int 1) |
| (match_operand 3 "const_int_operand" "n")) |
| (const_int 0)]) |
| (match_operand:SI 4 "reg_or_0_operand" "rI") |
| (match_operand:SI 5 "reg_or_int5_operand" "rJ"))) |
| (clobber (reg:CC CC_REGNUM))] |
| "(TARGET_V850E_UP)" |
| "tst1 %3,%2 ; cmov %C1,%5,%z4,%0" |
| [(set_attr "length" "8")]) |
| |
| ;; Matching for sasf requires combining 4 instructions, so we provide a |
| ;; dummy pattern to match the first 3, which will always be turned into the |
| ;; second pattern by subsequent combining. As above, we must include the |
| ;; comparison to avoid input reloads in an insn using cc0. |
| |
| ;; We could expose the setting of the condition codes here. |
| ;; However, I haven't seen this pattern used, so I'm not going |
| ;; to bother. |
| (define_insn "*sasf" |
| [(set (match_operand:SI 0 "register_operand" "=r") |
| (ior:SI |
| (match_operator 1 "comparison_operator" |
| [(match_operand:SI 3 "register_operand" "r") |
| (match_operand:SI 4 "reg_or_int5_operand" "rJ")]) |
| (ashift:SI (match_operand:SI 2 "register_operand" "0") |
| (const_int 1)))) |
| (clobber (reg:CC CC_REGNUM))] |
| "(TARGET_V850E_UP)" |
| "cmp %4,%3 ; sasf %c1,%0" |
| [(set_attr "length" "6")]) |
| |
| (define_split |
| [(set (match_operand:SI 0 "register_operand" "") |
| (if_then_else:SI |
| (match_operator 1 "comparison_operator" |
| [(match_operand:SI 4 "register_operand" "") |
| (match_operand:SI 5 "reg_or_int5_operand" "")]) |
| (match_operand:SI 2 "const_int_operand" "") |
| (match_operand:SI 3 "const_int_operand" ""))) |
| (clobber (reg:CC CC_REGNUM))] |
| "(TARGET_V850E_UP) |
| && ((INTVAL (operands[2]) ^ INTVAL (operands[3])) == 1) |
| && ((INTVAL (operands[2]) + INTVAL (operands[3])) != 1) |
| && (GET_CODE (operands[5]) == CONST_INT |
| || REGNO (operands[0]) != REGNO (operands[5])) |
| && REGNO (operands[0]) != REGNO (operands[4])" |
| [(set (match_dup 0) (match_dup 6)) |
| (parallel [(set (match_dup 0) |
| (ior:SI (match_op_dup 7 [(match_dup 4) (match_dup 5)]) |
| (ashift:SI (match_dup 0) (const_int 1)))) |
| (clobber (reg:CC CC_REGNUM))])] |
| { |
| operands[6] = GEN_INT (INTVAL (operands[2]) >> 1); |
| if (INTVAL (operands[2]) & 0x1) |
| operands[7] = operands[1]; |
| else |
| operands[7] = gen_rtx_fmt_ee (reverse_condition (GET_CODE (operands[1])), |
| GET_MODE (operands[1]), |
| XEXP (operands[1], 0), XEXP (operands[1], 1)); |
| }) |
| |
| ;; --------------------------------------------------------------------- |
| ;; BYTE SWAP INSTRUCTIONS |
| ;; --------------------------------------------------------------------- |
| (define_expand "rotlhi3" |
| [(parallel [(set (match_operand:HI 0 "register_operand" "") |
| (rotate:HI (match_operand:HI 1 "register_operand" "") |
| (match_operand:HI 2 "const_int_operand" ""))) |
| (clobber (reg:CC CC_REGNUM))])] |
| "(TARGET_V850E_UP)" |
| { |
| if (INTVAL (operands[2]) != 8) |
| FAIL; |
| }) |
| |
| (define_insn "*rotlhi3_8" |
| [(set (match_operand:HI 0 "register_operand" "=r") |
| (rotate:HI (match_operand:HI 1 "register_operand" "r") |
| (const_int 8))) |
| (clobber (reg:CC CC_REGNUM))] |
| "(TARGET_V850E_UP)" |
| "bsh %1,%0" |
| [(set_attr "length" "4")]) |
| |
| (define_expand "rotlsi3" |
| [(parallel [(set (match_operand:SI 0 "register_operand" "") |
| (rotate:SI (match_operand:SI 1 "register_operand" "") |
| (match_operand:SI 2 "const_int_operand" ""))) |
| (clobber (reg:CC CC_REGNUM))])] |
| "(TARGET_V850E_UP)" |
| { |
| if (INTVAL (operands[2]) != 16) |
| FAIL; |
| }) |
| |
| (define_insn "rotlsi3_a" |
| [(set (match_operand:SI 0 "register_operand" "=r") |
| (match_operator:SI 4 "ior_operator" |
| [(ashift:SI (match_operand:SI 1 "register_operand" "r") |
| (match_operand:SI 2 "const_int_operand" "n")) |
| (lshiftrt:SI (match_dup 1) |
| (match_operand:SI 3 "const_int_operand" "n"))])) |
| (clobber (reg:CC CC_REGNUM))] |
| "TARGET_V850E3V5_UP && (INTVAL (operands[2]) + INTVAL (operands[3]) == 32)" |
| "rotl %2, %1, %0" |
| [(set_attr "length" "4")]) |
| |
| (define_insn "rotlsi3_b" |
| [(set (match_operand:SI 0 "register_operand" "=r") |
| (match_operator:SI 4 "ior_operator" |
| [(lshiftrt:SI (match_operand:SI 1 "register_operand" "r") |
| (match_operand:SI 3 "const_int_operand" "n")) |
| (ashift:SI (match_dup 1) |
| (match_operand:SI 2 "const_int_operand" "n"))])) |
| (clobber (reg:CC CC_REGNUM))] |
| "TARGET_V850E3V5_UP && (INTVAL (operands[2]) + INTVAL (operands[3]) == 32)" |
| "rotl %2, %1, %0" |
| [(set_attr "length" "4")]) |
| |
| (define_insn "rotlsi3_v850e3v5" |
| [(set (match_operand:SI 0 "register_operand" "=r") |
| (rotate:SI (match_operand:SI 1 "register_operand" "r") |
| (match_operand:SI 2 "e3v5_shift_operand" "rn"))) |
| (clobber (reg:CC CC_REGNUM))] |
| "TARGET_V850E3V5_UP" |
| "rotl %2, %1, %0" |
| [(set_attr "length" "4")]) |
| |
| (define_insn "*rotlsi3_16" |
| [(set (match_operand:SI 0 "register_operand" "=r") |
| (rotate:SI (match_operand:SI 1 "register_operand" "r") |
| (const_int 16))) |
| (clobber (reg:CC CC_REGNUM))] |
| "(TARGET_V850E_UP)" |
| "hsw %1,%0" |
| [(set_attr "length" "4")]) |
| |
| ;; ---------------------------------------------------------------------- |
| ;; JUMP INSTRUCTIONS |
| ;; ---------------------------------------------------------------------- |
| |
| ;; Doloop |
| |
| (define_expand "doloop_begin" |
| [(use (match_operand 0 "" "")) ; loop pseudo |
| (use (match_operand 1 "" ""))] ; doloop_end pattern |
| "TARGET_V850E3V5_UP && TARGET_LOOP" |
| { |
| rtx loop_cnt = operands[0]; |
| gcc_assert (GET_MODE (loop_cnt) == SImode); |
| emit_insn (gen_fix_loop_counter (loop_cnt)); |
| DONE; |
| } |
| ) |
| |
| ;; Note the embedded arithmetic. That affects the condition codes! |
| (define_insn "fix_loop_counter" |
| [(unspec:SI [(match_operand:SI 0 "register_operand" "+r,!m") |
| (clobber (match_scratch:SI 1 "=X,r"))] UNSPEC_LOOP) |
| (clobber (reg:CC CC_REGNUM))] |
| "TARGET_V850E3V5_UP && TARGET_LOOP" |
| { |
| switch (which_alternative) |
| { |
| case 0: return "add 1, %0 # LOOP_BEGIN"; |
| case 1: return "ld.w %0, %1; add 1, %1; st.w %1, %0 # LOOP_BEGIN"; |
| default: gcc_unreachable (); |
| } |
| } |
| [(set_attr "length" "2,6")]) |
| |
| (define_expand "doloop_end" |
| [(use (match_operand 0 "" "")) ; loop pseudo |
| (use (match_operand 1 "" ""))] ; label |
| "TARGET_V850E3V5_UP && TARGET_LOOP" |
| { |
| rtx loop_cnt = operands[0]; |
| rtx label = operands[1]; |
| |
| if (GET_MODE (loop_cnt) != SImode) |
| FAIL; |
| |
| emit_jump_insn (gen_doloop_end_internal_loop (label, loop_cnt)); |
| DONE; |
| } |
| ) |
| |
| (define_insn "doloop_end_internal_loop" |
| [(set (pc) |
| (if_then_else (ne (match_operand:SI 1 "register_operand" "+r,!m") |
| (const_int 0)) |
| (label_ref (match_operand 0 "" "")) |
| (pc))) |
| (set (match_dup 1) (plus:SI (match_dup 1) (const_int -1))) |
| (clobber (match_scratch:SI 2 "=X,r")) |
| (clobber (reg:CC CC_REGNUM))] |
| "TARGET_V850E3V5_UP && TARGET_LOOP" |
| { |
| switch (which_alternative) |
| { |
| case 0: |
| if (get_attr_length (insn) == 4) |
| return "loop %1, %0 # LOOP.1.0"; |
| |
| return "add -1, %1; bne %l0 # LOOP.1.1"; |
| case 1: |
| return "ld.w %1, %2; add -1, %2; st.w %2, %1; bne %l0 # LOOP.2.1"; |
| default: |
| gcc_unreachable (); |
| } |
| } |
| [(set (attr "length") |
| (if_then_else (lt (abs (minus (match_dup 0) (pc))) |
| (const_int 65534)) |
| (const_int 4) |
| (const_int 14)))]) |
| |
| ;; Conditional jump instructions |
| |
| (define_insn_and_split "*cbranchsi4_insn" |
| [(set (pc) |
| (if_then_else (match_operator 0 "comparison_operator" |
| [(match_operand:SI 1 "register_operand" "r") |
| (match_operand:SI 2 "reg_or_int5_operand" "rJ")]) |
| (label_ref (match_operand 3 "" "")) |
| (pc)))] |
| "" |
| "#" |
| "reload_completed" |
| [(set (reg:CC CC_REGNUM) |
| (compare:CC (match_dup 1) (match_dup 2))) |
| (set (pc) |
| (if_then_else (match_op_dup 0 |
| [(reg:CC CC_REGNUM) (const_int 0)]) |
| (label_ref (match_dup 3)) |
| (pc)))] |
| "") |
| |
| (define_insn "*branch_normal" |
| [(set (pc) |
| (if_then_else (match_operator 1 "comparison_operator" |
| [(reg CC_REGNUM) (const_int 0)]) |
| (label_ref (match_operand 0 "" "")) |
| (pc)))] |
| "reload_completed" |
| { |
| bool nzmode = GET_MODE (XEXP (operands[1], 0)) == CCNZmode; |
| if (get_attr_length (insn) == 2) |
| { |
| if (nzmode) |
| return "b%d1 %l0"; |
| else |
| return "b%b1 %l0"; |
| } |
| if (TARGET_V850E3V5_UP && get_attr_length (insn) == 4) |
| { |
| if (nzmode) |
| return "b%d1 %l0"; |
| else |
| return "b%b1 %l0"; |
| } |
| if (nzmode) |
| return "b%D1 .+6 ; jr %l0"; |
| else |
| return "b%B1 .+6 ; jr %l0"; |
| } |
| [(set (attr "length") |
| (if_then_else (lt (abs (minus (match_dup 0) (pc))) |
| (const_int 256)) |
| (const_int 2) |
| (if_then_else (lt (abs (minus (match_dup 0) (pc))) |
| (const_int 65536)) |
| (const_int 4) |
| (const_int 6))))]) |
| |
| (define_insn "*branch_invert" |
| [(set (pc) |
| (if_then_else (match_operator 1 "comparison_operator" |
| [(reg CC_REGNUM) (const_int 0)]) |
| (pc) |
| (label_ref (match_operand 0 "" ""))))] |
| "reload_completed" |
| { |
| bool nzmode = GET_MODE (XEXP (operands[1], 0)) == CCNZmode; |
| |
| if (get_attr_length (insn) == 2) |
| { |
| if (nzmode) |
| return "b%D1 %l0"; |
| else |
| return "b%B1 %l0"; |
| } |
| |
| if (TARGET_V850E3V5_UP && get_attr_length (insn) == 4) |
| { |
| if (nzmode) |
| return "b%D1 %l0"; |
| else |
| return "b%B1 %l0"; |
| } |
| |
| if (nzmode) |
| return "b%d1 .+6 ; jr %l0"; |
| else |
| return "b%b1 .+6 ; jr %l0"; |
| } |
| [(set (attr "length") |
| (if_then_else (lt (abs (minus (match_dup 0) (pc))) |
| (const_int 256)) |
| (const_int 2) |
| (if_then_else (lt (abs (minus (match_dup 0) (pc))) |
| (const_int 65536)) |
| (const_int 4) |
| (const_int 6))))]) |
| |
| ;; Unconditional and other jump instructions. |
| |
| (define_insn "jump" |
| [(set (pc) |
| (label_ref (match_operand 0 "" "")))] |
| "" |
| { |
| if (get_attr_length (insn) == 2) |
| return "br %0"; |
| else |
| return "jr %0"; |
| } |
| [(set (attr "length") |
| (if_then_else (lt (abs (minus (match_dup 0) (pc))) |
| (const_int 256)) |
| (const_int 2) |
| (const_int 4)))]) |
| |
| (define_insn "indirect_jump" |
| [(set (pc) (match_operand:SI 0 "register_operand" "r"))] |
| "" |
| "jmp %0" |
| [(set_attr "length" "2")]) |
| |
| (define_insn "tablejump" |
| [(set (pc) (match_operand:SI 0 "register_operand" "r")) |
| (use (label_ref (match_operand 1 "" "")))] |
| "" |
| "jmp %0" |
| [(set_attr "length" "2")]) |
| |
| (define_insn "switch" |
| [(set (pc) |
| (plus:SI |
| (sign_extend:SI |
| (mem:HI |
| (plus:SI (ashift:SI (match_operand:SI 0 "register_operand" "r") |
| (const_int 1)) |
| (label_ref (match_operand 1 "" ""))))) |
| (label_ref (match_dup 1))))] |
| "(TARGET_V850E_UP)" |
| "switch %0" |
| [(set_attr "length" "2")]) |
| |
| (define_expand "casesi" |
| [(match_operand:SI 0 "register_operand" "") |
| (match_operand:SI 1 "register_operand" "") |
| (match_operand:SI 2 "register_operand" "") |
| (match_operand 3 "" "") (match_operand 4 "" "")] |
| "" |
| { |
| rtx reg = gen_reg_rtx (SImode); |
| rtx tableaddress = gen_reg_rtx (SImode); |
| rtx test; |
| rtx mem; |
| |
| /* Subtract the lower bound from the index. */ |
| emit_insn (gen_subsi3 (reg, operands[0], operands[1])); |
| |
| /* Compare the result against the number of table entries; |
| branch to the default label if out of range of the table. */ |
| test = gen_rtx_fmt_ee (GTU, VOIDmode, reg, operands[2]); |
| emit_jump_insn (gen_cbranchsi4 (test, reg, operands[2], operands[4])); |
| |
| /* Shift index for the table array access. */ |
| emit_insn (gen_ashlsi3 (reg, reg, GEN_INT (TARGET_BIG_SWITCH ? 2 : 1))); |
| /* Load the table address into a pseudo. */ |
| emit_insn (gen_movsi (tableaddress, |
| gen_rtx_LABEL_REF (Pmode, operands[3]))); |
| /* Add the table address to the index. */ |
| emit_insn (gen_addsi3 (reg, reg, tableaddress)); |
| /* Load the table entry. */ |
| mem = gen_const_mem (CASE_VECTOR_MODE, reg); |
| if (! TARGET_BIG_SWITCH) |
| { |
| rtx reg2 = gen_reg_rtx (HImode); |
| emit_insn (gen_movhi (reg2, mem)); |
| emit_insn (gen_extendhisi2 (reg, reg2)); |
| } |
| else |
| emit_insn (gen_movsi (reg, mem)); |
| /* Add the table address. */ |
| emit_insn (gen_addsi3 (reg, reg, tableaddress)); |
| /* Branch to the switch label. */ |
| emit_jump_insn (gen_tablejump (reg, operands[3])); |
| DONE; |
| }) |
| |
| ;; Call subroutine with no return value. |
| |
| (define_expand "call" |
| [(call (match_operand:QI 0 "general_operand" "") |
| (match_operand:SI 1 "general_operand" ""))] |
| "" |
| { |
| if (! call_address_operand (XEXP (operands[0], 0), QImode) |
| || TARGET_LONG_CALLS) |
| XEXP (operands[0], 0) = force_reg (SImode, XEXP (operands[0], 0)); |
| if (TARGET_LONG_CALLS) |
| emit_call_insn (gen_call_internal_long (XEXP (operands[0], 0), operands[1])); |
| else |
| emit_call_insn (gen_call_internal_short (XEXP (operands[0], 0), operands[1])); |
| |
| DONE; |
| }) |
| |
| (define_insn "call_internal_short" |
| [(call (mem:QI (match_operand:SI 0 "call_address_operand" "S,r")) |
| (match_operand:SI 1 "general_operand" "g,g")) |
| (clobber (reg:CC CC_REGNUM)) |
| (clobber (reg:SI 31))] |
| "! TARGET_LONG_CALLS" |
| { |
| if (which_alternative == 1) |
| { |
| if (TARGET_V850E3V5_UP) |
| return "jarl [%0], r31"; |
| |
| return "jarl .+4, r31 ; add 4, r31 ; jmp %0"; |
| } |
| |
| return "jarl %0, r31"; |
| } |
| [(set_attr "length" "4,8")]) |
| |
| (define_insn "call_internal_long" |
| [(call (mem:QI (match_operand:SI 0 "call_address_operand" "S,r")) |
| (match_operand:SI 1 "general_operand" "g,g")) |
| (clobber (reg:CC CC_REGNUM)) |
| (clobber (reg:SI 31))] |
| "TARGET_LONG_CALLS" |
| { |
| if (which_alternative == 0) |
| { |
| if (GET_CODE (operands[0]) == REG) |
| return "jarl %0,r31"; |
| |
| if (TARGET_V850E3V5_UP) |
| return "mov hilo(%0), r11 ; jarl [r11], r31"; |
| |
| return "movhi hi(%0), r0, r11 ; movea lo(%0), r11, r11 ; jarl .+4,r31 ; add 4, r31 ; jmp r11"; |
| } |
| |
| if (TARGET_V850E3V5_UP) |
| return "jarl [%0], r31"; |
| |
| return "jarl .+4,r31 ; add 4,r31 ; jmp %0"; |
| } |
| [(set_attr "length" "16,8")]) |
| |
| ;; Call subroutine, returning value in operand 0 |
| ;; (which must be a hard register). |
| |
| (define_expand "call_value" |
| [(set (match_operand 0 "" "") |
| (call (match_operand:QI 1 "general_operand" "") |
| (match_operand:SI 2 "general_operand" "")))] |
| "" |
| { |
| if (! call_address_operand (XEXP (operands[1], 0), QImode) |
| || TARGET_LONG_CALLS) |
| XEXP (operands[1], 0) = force_reg (SImode, XEXP (operands[1], 0)); |
| if (TARGET_LONG_CALLS) |
| emit_call_insn (gen_call_value_internal_long (operands[0], |
| XEXP (operands[1], 0), |
| operands[2])); |
| else |
| emit_call_insn (gen_call_value_internal_short (operands[0], |
| XEXP (operands[1], 0), |
| operands[2])); |
| DONE; |
| }) |
| |
| (define_insn "call_value_internal_short" |
| [(set (match_operand 0 "" "=r,r") |
| (call (mem:QI (match_operand:SI 1 "call_address_operand" "S,r")) |
| (match_operand:SI 2 "general_operand" "g,g"))) |
| (clobber (reg:CC CC_REGNUM)) |
| (clobber (reg:SI 31))] |
| "! TARGET_LONG_CALLS" |
| { |
| if (which_alternative == 1) |
| { |
| if (TARGET_V850E3V5_UP) |
| return "jarl [%1], r31"; |
| |
| return "jarl .+4, r31 ; add 4, r31 ; jmp %1"; |
| } |
| |
| return "jarl %1, r31"; |
| } |
| [(set_attr "length" "4,8")]) |
| |
| (define_insn "call_value_internal_long" |
| [(set (match_operand 0 "" "=r,r") |
| (call (mem:QI (match_operand:SI 1 "call_address_operand" "S,r")) |
| (match_operand:SI 2 "general_operand" "g,g"))) |
| (clobber (reg:CC CC_REGNUM)) |
| (clobber (reg:SI 31))] |
| "TARGET_LONG_CALLS" |
| { |
| if (which_alternative == 0) |
| { |
| if (GET_CODE (operands[1]) == REG) |
| return "jarl %1, r31"; |
| |
| /* Reload can generate this pattern.... */ |
| if (TARGET_V850E3V5_UP) |
| return "mov hilo(%1), r11 ; jarl [r11], r31"; |
| |
| return "movhi hi(%1), r0, r11 ; movea lo(%1), r11, r11 ; jarl .+4, r31 ; add 4, r31 ; jmp r11"; |
| } |
| |
| if (TARGET_V850E3V5_UP) |
| return "jarl [%1], r31"; |
| |
| return "jarl .+4, r31 ; add 4, r31 ; jmp %1"; |
| } |
| [(set_attr "length" "16,8")]) |
| |
| (define_insn "nop" |
| [(const_int 0)] |
| "" |
| "nop" |
| [(set_attr "length" "2")]) |
| |
| ;; ---------------------------------------------------------------------- |
| ;; EXTEND INSTRUCTIONS |
| ;; ---------------------------------------------------------------------- |
| |
| ;; We only need the CC clobber because of the andi alternative |
| (define_insn "*zero_extendhisi2_v850e" |
| [(set (match_operand:SI 0 "register_operand" "=r,r,r,r") |
| (zero_extend:SI |
| (match_operand:HI 1 "nonimmediate_operand" "0,r,T,m"))) |
| (clobber (reg:CC CC_REGNUM))] |
| "(TARGET_V850E_UP)" |
| "@ |
| zxh %0 |
| andi 65535,%1,%0 |
| sld.hu %1,%0 |
| ld.hu %1,%0" |
| [(set_attr "length" "2,4,2,4")]) |
| |
| (define_insn "*zero_extendhisi2_v850" |
| [(set (match_operand:SI 0 "register_operand" "=r") |
| (zero_extend:SI |
| (match_operand:HI 1 "register_operand" "r"))) |
| (clobber (reg:CC CC_REGNUM))] ;; A lie, but we have to match the expander |
| "" |
| "andi 65535,%1,%0" |
| [(set_attr "length" "4")]) |
| |
| (define_insn "*zero_extendhisi2_v850_set_flags" |
| [(set (reg:CCNZ CC_REGNUM) |
| (compare:CCNZ (zero_extend:SI (match_operand:HI 1 "register_operand" "r")) |
| (const_int 0))) |
| (set (match_operand:SI 0 "register_operand" "=r") |
| (zero_extend:SI (match_dup 1)))] |
| "reload_completed" |
| "andi 65535,%1,%0" |
| [(set_attr "length" "4")]) |
| |
| (define_expand "zero_extendhisi2" |
| [(parallel [(set (match_operand:SI 0 "register_operand") |
| (zero_extend:SI |
| (match_operand:HI 1 "nonimmediate_operand"))) |
| (clobber (reg:CC CC_REGNUM))])] |
| "" |
| { |
| if (! (TARGET_V850E_UP)) |
| operands[1] = force_reg (HImode, operands[1]); |
| }) |
| |
| (define_insn "*zero_extendqisi2_v850e" |
| [(set (match_operand:SI 0 "register_operand" "=r,r,r,r") |
| (zero_extend:SI |
| (match_operand:QI 1 "nonimmediate_operand" "0,r,T,m"))) |
| (clobber (reg:CC CC_REGNUM))] |
| "(TARGET_V850E_UP)" |
| "@ |
| zxb %0 |
| andi 255,%1,%0 |
| sld.bu %1,%0 |
| ld.bu %1,%0" |
| [(set_attr "length" "2,4,2,4")]) |
| |
| (define_insn "*zero_extendqisi2_v850" |
| [(set (match_operand:SI 0 "register_operand" "=r") |
| (zero_extend:SI |
| (match_operand:QI 1 "register_operand" "r"))) |
| (clobber (reg:CC CC_REGNUM))] ;; A lie, but we have to match the expander |
| "" |
| "andi 255,%1,%0" |
| [(set_attr "length" "4")]) |
| |
| (define_insn "*zero_extendqisi2_v850_set_flags" |
| [(set (reg:CCNZ CC_REGNUM) |
| (compare:CCNZ (zero_extend:SI (match_operand:QI 1 "register_operand" "r")) |
| (const_int 0))) |
| (set (match_operand:SI 0 "register_operand" "=r") |
| (zero_extend:SI (match_dup 1)))] |
| "reload_completed" |
| "andi 255,%1,%0" |
| [(set_attr "length" "4")]) |
| |
| (define_expand "zero_extendqisi2" |
| [(parallel [(set (match_operand:SI 0 "register_operand") |
| (zero_extend:SI |
| (match_operand:QI 1 "nonimmediate_operand"))) |
| (clobber (reg:CC CC_REGNUM))])] |
| "" |
| { |
| if (! (TARGET_V850E_UP)) |
| operands[1] = force_reg (QImode, operands[1]); |
| }) |
| |
| ;;- sign extension instructions |
| |
| ;; ??? The extendhisi2 pattern should not emit shifts for v850e? |
| |
| (define_insn "*extendhisi_insn" |
| [(set (match_operand:SI 0 "register_operand" "=r,r,r") |
| (sign_extend:SI (match_operand:HI 1 "nonimmediate_operand" "0,Q,m")))] |
| "(TARGET_V850E_UP)" |
| "@ |
| sxh %0 |
| sld.h %1,%0 |
| ld.h %1,%0" |
| [(set_attr "length" "2,2,4")]) |
| |
| ;; ??? This is missing a sign extend from memory pattern to match the ld.h |
| ;; instruction. |
| |
| (define_expand "extendhisi2" |
| [(parallel [(set (match_dup 2) |
| (ashift:SI (match_operand:HI 1 "register_operand" "") |
| (const_int 16))) |
| (clobber (reg:CC CC_REGNUM))]) |
| (parallel [(set (match_operand:SI 0 "register_operand" "") |
| (ashiftrt:SI (match_dup 2) |
| (const_int 16))) |
| (clobber (reg:CC CC_REGNUM))])] |
| "" |
| { |
| operands[1] = gen_lowpart (SImode, operands[1]); |
| operands[2] = gen_reg_rtx (SImode); |
| }) |
| |
| ;; ??? The extendqisi2 pattern should not emit shifts for v850e? |
| |
| (define_insn "*extendqisi_insn" |
| [(set (match_operand:SI 0 "register_operand" "=r,r,r") |
| (sign_extend:SI (match_operand:QI 1 "nonimmediate_operand" "0,Q,m")))] |
| "(TARGET_V850E_UP)" |
| "@ |
| sxb %0 |
| sld.b %1,%0 |
| ld.b %1,%0" |
| [(set_attr "length" "2,2,4")]) |
| |
| ;; ??? This is missing a sign extend from memory pattern to match the ld.b |
| ;; instruction. |
| |
| (define_expand "extendqisi2" |
| [(parallel [(set (match_dup 2) |
| (ashift:SI (match_operand:QI 1 "register_operand" "") |
| (const_int 24))) |
| (clobber (reg:CC CC_REGNUM))]) |
| (parallel [(set (match_operand:SI 0 "register_operand" "") |
| (ashiftrt:SI (match_dup 2) |
| (const_int 24))) |
| (clobber (reg:CC CC_REGNUM))])] |
| "" |
| { |
| operands[1] = gen_lowpart (SImode, operands[1]); |
| operands[2] = gen_reg_rtx (SImode); |
| }) |
| |
| ;; ---------------------------------------------------------------------- |
| ;; SHIFTS |
| ;; ---------------------------------------------------------------------- |
| |
| (define_insn_and_split "ashlsi3" |
| [(set (match_operand:SI 0 "register_operand" "=r,r") |
| (ashift:SI |
| (match_operand:SI 1 "register_operand" "0,0") |
| (match_operand:SI 2 "nonmemory_operand" "r,N")))] |
| "" |
| "#" |
| "reload_completed" |
| [(parallel [(set (match_dup 0) (ashift:SI (match_dup 1) (match_dup 2))) |
| (clobber (reg:CC CC_REGNUM))])]) |
| |
| (define_insn "ashlsi3_clobber_flags" |
| [(set (match_operand:SI 0 "register_operand" "=r,r") |
| (ashift:SI |
| (match_operand:SI 1 "register_operand" "0,0") |
| (match_operand:SI 2 "nonmemory_operand" "r,N"))) |
| (clobber (reg:CC CC_REGNUM))] |
| "" |
| "@ |
| shl %2,%0 |
| shl %2,%0" |
| [(set_attr "length" "4,2")]) |
| |
| (define_insn "ashlsi3_set_flags" |
| [(set (reg:CCNZ CC_REGNUM) |
| (compare:CCNZ (ashift:SI (match_operand:SI 1 "register_operand" "0,0") |
| (match_operand:SI 2 "nonmemory_operand" "r,N")) |
| (const_int 0))) |
| (set (match_operand:SI 0 "register_operand" "=r,r") |
| (ashift:SI (match_dup 1) (match_dup 2)))] |
| "reload_completed" |
| "@ |
| shl %2,%0 |
| shl %2,%0" |
| [(set_attr "length" "4,2")]) |
| |
| (define_insn "ashlsi3_v850e2_clobber_flags" |
| [(set (match_operand:SI 0 "register_operand" "=r") |
| (ashift:SI |
| (match_operand:SI 1 "register_operand" "r") |
| (match_operand:SI 2 "nonmemory_operand" "r"))) |
| (clobber (reg:CC CC_REGNUM))] |
| "TARGET_V850E2_UP" |
| "shl %2,%1,%0" |
| [(set_attr "length" "4")]) |
| |
| (define_insn "ashlsi3_v850e2_set_flags" |
| [(set (reg:CCNZ CC_REGNUM) |
| (compare:CCNZ (ashift:SI (match_operand:SI 1 "register_operand" "r") |
| (match_operand:SI 2 "nonmemory_operand" "r")) |
| (const_int 0))) |
| (set (match_operand:SI 0 "register_operand" "=r") |
| (ashift:SI (match_dup 1) (match_dup 2)))] |
| "reload_completed && TARGET_V850E2_UP" |
| "shl %2,%1,%0" |
| [(set_attr "length" "4")]) |
| |
| (define_insn_and_split "lshrsi3" |
| [(set (match_operand:SI 0 "register_operand" "=r,r") |
| (lshiftrt:SI |
| (match_operand:SI 1 "register_operand" "0,0") |
| (match_operand:SI 2 "nonmemory_operand" "r,N")))] |
| "" |
| "#" |
| "reload_completed" |
| [(parallel [(set (match_dup 0) (lshiftrt:SI (match_dup 1) (match_dup 2))) |
| (clobber (reg:CC CC_REGNUM))])]) |
| |
| (define_insn "lshrsi3_clobber_flags" |
| [(set (match_operand:SI 0 "register_operand" "=r,r") |
| (lshiftrt:SI |
| (match_operand:SI 1 "register_operand" "0,0") |
| (match_operand:SI 2 "nonmemory_operand" "r,N"))) |
| (clobber (reg:CC CC_REGNUM))] |
| "" |
| "@ |
| shr %2,%0 |
| shr %2,%0" |
| [(set_attr "length" "4,2")]) |
| |
| (define_insn "lshrsi3_set_flags" |
| [(set (reg:CC CC_REGNUM) |
| (compare:CC (lshiftrt:SI (match_operand:SI 1 "register_operand" "0,0") |
| (match_operand:SI 2 "nonmemory_operand" "r,N")) |
| (const_int 0))) |
| (set (match_operand:SI 0 "register_operand" "=r,r") |
| (lshiftrt:SI (match_dup 1) (match_dup 2)))] |
| "reload_completed" |
| "@ |
| shr %2,%0 |
| shr %2,%0" |
| [(set_attr "length" "4,2")]) |
| |
| (define_insn "lshrsi3_v850e2_clobber_flags" |
| [(set (match_operand:SI 0 "register_operand" "=r") |
| (lshiftrt:SI |
| (match_operand:SI 1 "register_operand" "r") |
| (match_operand:SI 2 "nonmemory_operand" "r"))) |
| (clobber (reg:CC CC_REGNUM))] |
| "TARGET_V850E2_UP" |
| "shr %2,%1,%0" |
| [(set_attr "length" "4")]) |
| |
| (define_insn "lshrsi3_v850e2_set_flags" |
| [(set (reg:CC CC_REGNUM) |
| (compare:CC (lshiftrt:SI (match_operand:SI 1 "register_operand" "r") |
| (match_operand:SI 2 "nonmemory_operand" "r")) |
| (const_int 0))) |
| (set (match_operand:SI 0 "register_operand" "=r") |
| (lshiftrt:SI (match_dup 1) (match_dup 2)))] |
| "reload_completed && TARGET_V850E2_UP" |
| "shr %2,%1,%0" |
| [(set_attr "length" "4")]) |
| |
| (define_insn_and_split "ashrsi3" |
| [(set (match_operand:SI 0 "register_operand" "=r,r") |
| (ashiftrt:SI |
| (match_operand:SI 1 "register_operand" "0,0") |
| (match_operand:SI 2 "nonmemory_operand" "r,N")))] |
| "" |
| "#" |
| "reload_completed" |
| [(parallel [(set (match_dup 0) (ashiftrt:SI (match_dup 1) (match_dup 2))) |
| (clobber (reg:CC CC_REGNUM))])]) |
| |
| (define_insn "ashrsi3_clobber_flags" |
| [(set (match_operand:SI 0 "register_operand" "=r,r") |
| (ashiftrt:SI |
| (match_operand:SI 1 "register_operand" "0,0") |
| (match_operand:SI 2 "nonmemory_operand" "r,N"))) |
| (clobber (reg:CC CC_REGNUM))] |
| "" |
| "@ |
| sar %2,%0 |
| sar %2,%0" |
| [(set_attr "length" "4,2")]) |
| |
| (define_insn "ashrsi3_set_flags" |
| [(set (reg:CC CC_REGNUM) |
| (compare:CC (ashiftrt:SI (match_operand:SI 1 "register_operand" "0,0") |
| (match_operand:SI 2 "nonmemory_operand" "r,N")) |
| (const_int 0))) |
| (set (match_operand:SI 0 "register_operand" "=r,r") |
| (ashiftrt:SI (match_dup 1) (match_dup 2)))] |
| "reload_completed" |
| "@ |
| sar %2,%0 |
| sar %2,%0" |
| [(set_attr "length" "4,2")]) |
| |
| (define_insn "ashrsi3_v850e2_clobber_flags" |
| [(set (match_operand:SI 0 "register_operand" "=r") |
| (ashiftrt:SI |
| (match_operand:SI 1 "register_operand" "r") |
| (match_operand:SI 2 "nonmemory_operand" "r"))) |
| (clobber (reg:CC CC_REGNUM))] |
| "TARGET_V850E2_UP" |
| "sar %2,%1,%0" |
| [(set_attr "length" "4")]) |
| |
| (define_insn "ashrsi3_v850e2_set_flags" |
| [(set (reg:CC CC_REGNUM) |
| (compare:CC (ashiftrt:SI (match_operand:SI 1 "register_operand" "r") |
| (match_operand:SI 2 "nonmemory_operand" "r")) |
| (const_int 0))) |
| (set (match_operand:SI 0 "register_operand" "=r") |
| (ashiftrt:SI (match_dup 1) (match_dup 2)))] |
| "reload_completed && TARGET_V850E2_UP" |
| "sar %2,%1,%0" |
| [(set_attr "length" "4")]) |
| |
| ;; ---------------------------------------------------------------------- |
| ;; FIND FIRST BIT INSTRUCTION |
| ;; ---------------------------------------------------------------------- |
| |
| (define_insn "ffssi2" |
| [(set (match_operand:SI 0 "register_operand" "=r") |
| (ffs:SI (match_operand:SI 1 "register_operand" "r"))) |
| (clobber (reg:CC CC_REGNUM))] |
| "TARGET_V850E2_UP" |
| "sch1r %1,%0" |
| [(set_attr "length" "4")]) |
| |
| ;; ---------------------------------------------------------------------- |
| ;; PROLOGUE/EPILOGUE |
| ;; ---------------------------------------------------------------------- |
| (define_expand "prologue" |
| [(const_int 0)] |
| "" |
| { |
| expand_prologue (); |
| DONE; |
| }) |
| |
| (define_expand "epilogue" |
| [(return)] |
| "" |
| { |
| expand_epilogue (); |
| DONE; |
| }) |
| |
| (define_insn "return_simple" |
| [(return)] |
| "reload_completed" |
| "jmp [r31]" |
| [(set_attr "length" "2")]) |
| |
| (define_insn "return_internal" |
| [(return) |
| (use (reg:SI 31))] |
| "" |
| "jmp [r31]" |
| [(set_attr "length" "2")]) |
| |
| ;; ---------------------------------------------------------------------- |
| ;; v850e2V3 floating-point hardware support |
| ;; ---------------------------------------------------------------------- |
| |
| |
| (define_insn "addsf3" |
| [(set (match_operand:SF 0 "register_operand" "=r") |
| (plus:SF (match_operand:SF 1 "register_operand" "r") |
| (match_operand:SF 2 "register_operand" "r")))] |
| "TARGET_USE_FPU" |
| "addf.s %1,%2,%0" |
| [(set_attr "length" "4") |
| (set_attr "type" "fpu")]) |
| |
| (define_insn "adddf3" |
| [(set (match_operand:DF 0 "even_reg_operand" "=r") |
| (plus:DF (match_operand:DF 1 "even_reg_operand" "r") |
| (match_operand:DF 2 "even_reg_operand" "r")))] |
| "TARGET_USE_FPU" |
| "addf.d %1,%2,%0" |
| [(set_attr "length" "4") |
| (set_attr "type" "fpu")]) |
| |
| (define_insn "subsf3" |
| [(set (match_operand:SF 0 "register_operand" "=r") |
| (minus:SF (match_operand:SF 1 "register_operand" "r") |
| (match_operand:SF 2 "register_operand" "r")))] |
| "TARGET_USE_FPU" |
| "subf.s %2,%1,%0" |
| [(set_attr "length" "4") |
| (set_attr "type" "fpu")]) |
| |
| (define_insn "subdf3" |
| [(set (match_operand:DF 0 "even_reg_operand" "=r") |
| (minus:DF (match_operand:DF 1 "even_reg_operand" "r") |
| (match_operand:DF 2 "even_reg_operand" "r")))] |
| "TARGET_USE_FPU" |
| "subf.d %2,%1,%0" |
| [(set_attr "length" "4") |
| (set_attr "type" "fpu")]) |
| |
| (define_insn "mulsf3" |
| [(set (match_operand:SF 0 "register_operand" "=r") |
| (mult:SF (match_operand:SF 1 "register_operand" "r") |
| (match_operand:SF 2 "register_operand" "r")))] |
| "TARGET_USE_FPU" |
| "mulf.s %1,%2,%0" |
| [(set_attr "length" "4") |
| (set_attr "type" "fpu")]) |
| |
| (define_insn "muldf3" |
| [(set (match_operand:DF 0 "even_reg_operand" "=r") |
| (mult:DF (match_operand:DF 1 "even_reg_operand" "r") |
| (match_operand:DF 2 "even_reg_operand" "r")))] |
| "TARGET_USE_FPU" |
| "mulf.d %1,%2,%0" |
| [(set_attr "length" "4") |
| (set_attr "type" "fpu")]) |
| |
| (define_insn "divsf3" |
| [(set (match_operand:SF 0 "register_operand" "=r") |
| (div:SF (match_operand:SF 1 "register_operand" "r") |
| (match_operand:SF 2 "register_operand" "r")))] |
| "TARGET_USE_FPU" |
| "divf.s %2,%1,%0" |
| [(set_attr "length" "4") |
| (set_attr "type" "fpu")]) |
| |
| (define_insn "divdf3" |
| [(set (match_operand:DF 0 "register_operand" "=r") |
| (div:DF (match_operand:DF 1 "even_reg_operand" "r") |
| (match_operand:DF 2 "even_reg_operand" "r")))] |
| "TARGET_USE_FPU" |
| "divf.d %2,%1,%0" |
| [(set_attr "length" "4") |
| (set_attr "type" "fpu")]) |
| |
| (define_insn "minsf3" |
| [(set (match_operand:SF 0 "register_operand" "=r") |
| (smin:SF (match_operand:SF 1 "reg_or_0_operand" "r") |
| (match_operand:SF 2 "reg_or_0_operand" "r")))] |
| "TARGET_USE_FPU" |
| "minf.s %z1,%z2,%0" |
| [(set_attr "length" "4") |
| (set_attr "type" "fpu")]) |
| |
| (define_insn "mindf3" |
| [(set (match_operand:DF 0 "even_reg_operand" "=r") |
| (smin:DF (match_operand:DF 1 "even_reg_operand" "r") |
| (match_operand:DF 2 "even_reg_operand" "r")))] |
| "TARGET_USE_FPU" |
| "minf.d %1,%2,%0" |
| [(set_attr "length" "4") |
| (set_attr "type" "fpu")]) |
| |
| (define_insn "maxsf3" |
| [(set (match_operand:SF 0 "register_operand" "=r") |
| (smax:SF (match_operand:SF 1 "reg_or_0_operand" "r") |
| (match_operand:SF 2 "reg_or_0_operand" "r")))] |
| "TARGET_USE_FPU" |
| "maxf.s %z1,%z2,%0" |
| [(set_attr "length" "4") |
| (set_attr "type" "fpu")]) |
| |
| (define_insn "maxdf3" |
| [(set (match_operand:DF 0 "even_reg_operand" "=r") |
| (smax:DF (match_operand:DF 1 "even_reg_operand" "r") |
| (match_operand:DF 2 "even_reg_operand" "r")))] |
| "TARGET_USE_FPU" |
| "maxf.d %1,%2,%0" |
| [(set_attr "length" "4") |
| (set_attr "type" "fpu")]) |
| |
| (define_insn "abssf2" |
| [(set (match_operand:SF 0 "register_operand" "=r") |
| (abs:SF (match_operand:SF 1 "register_operand" "r")))] |
| "TARGET_USE_FPU" |
| "absf.s %1,%0" |
| [(set_attr "length" "4") |
| (set_attr "type" "fpu")]) |
| |
| (define_insn "absdf2" |
| [(set (match_operand:DF 0 "even_reg_operand" "=r") |
| (abs:DF (match_operand:DF 1 "even_reg_operand" "r")))] |
| "TARGET_USE_FPU" |
| "absf.d %1,%0" |
| [(set_attr "length" "4") |
| (set_attr "type" "fpu")]) |
| |
| (define_insn "negsf2" |
| [(set (match_operand:SF 0 "register_operand" "=r") |
| (neg:SF (match_operand:SF 1 "register_operand" "r")))] |
| "TARGET_USE_FPU" |
| "negf.s %1,%0" |
| [(set_attr "length" "4") |
| (set_attr "type" "fpu")]) |
| |
| (define_insn "negdf2" |
| [(set (match_operand:DF 0 "even_reg_operand" "=r") |
| (neg:DF (match_operand:DF 1 "even_reg_operand" "r")))] |
| "TARGET_USE_FPU" |
| "negf.d %1,%0" |
| [(set_attr "length" "4") |
| (set_attr "type" "fpu")]) |
| |
| ;; square-root |
| (define_insn "sqrtsf2" |
| [(set (match_operand:SF 0 "register_operand" "=r") |
| (sqrt:SF (match_operand:SF 1 "register_operand" "r")))] |
| "TARGET_USE_FPU" |
| "sqrtf.s %1,%0" |
| [(set_attr "length" "4") |
| (set_attr "type" "fpu")]) |
| |
| (define_insn "sqrtdf2" |
| [(set (match_operand:DF 0 "even_reg_operand" "=r") |
| (sqrt:DF (match_operand:DF 1 "even_reg_operand" "r")))] |
| "TARGET_USE_FPU" |
| "sqrtf.d %1,%0" |
| [(set_attr "length" "4") |
| (set_attr "type" "fpu")]) |
| |
| ;; float -> int |
| (define_insn "fix_truncsfsi2" |
| [(set (match_operand:SI 0 "register_operand" "=r") |
| (fix:SI (match_operand:SF 1 "register_operand" "r")))] |
| "TARGET_USE_FPU" |
| "trncf.sw %1,%0" |
| [(set_attr "length" "4") |
| (set_attr "type" "fpu")]) |
| |
| (define_insn "fixuns_truncsfsi2" |
| [(set (match_operand:SI 0 "register_operand" "=r") |
| (unsigned_fix:SI (match_operand:SF 1 "register_operand" "r")))] |
| "TARGET_USE_FPU" |
| "trncf.suw %1, %0" |
| [(set_attr "length" "4") |
| (set_attr "type" "fpu")]) |
| |
| (define_insn "fix_truncdfsi2" |
| [(set (match_operand:SI 0 "register_operand" "=r") |
| (fix:SI (match_operand:DF 1 "even_reg_operand" "r")))] |
| "TARGET_USE_FPU" |
| "trncf.dw %1,%0" |
| [(set_attr "length" "4") |
| (set_attr "type" "fpu")]) |
| |
| (define_insn "fixuns_truncdfsi2" |
| [(set (match_operand:SI 0 "register_operand" "=r") |
| (unsigned_fix:SI (match_operand:DF 1 "even_reg_operand" "r")))] |
| "TARGET_USE_FPU" |
| "trncf.duw %1, %0" |
| [(set_attr "length" "4") |
| (set_attr "type" "fpu")]) |
| |
| (define_insn "fix_truncsfdi2" |
| [(set (match_operand:DI 0 "register_operand" "=r") |
| (fix:DI (match_operand:SF 1 "register_operand" "r")))] |
| "TARGET_USE_FPU" |
| "trncf.sl %1, %0" |
| [(set_attr "length" "4") |
| (set_attr "type" "fpu")]) |
| |
| (define_insn "fixuns_truncsfdi2" |
| [(set (match_operand:DI 0 "register_operand" "=r") |
| (unsigned_fix:DI (match_operand:SF 1 "register_operand" "r")))] |
| "TARGET_USE_FPU" |
| "trncf.sul %1, %0" |
| [(set_attr "length" "4") |
| (set_attr "type" "fpu")]) |
| |
| (define_insn "fix_truncdfdi2" |
| [(set (match_operand:DI 0 "register_operand" "=r") |
| (fix:DI (match_operand:DF 1 "even_reg_operand" "r")))] |
| "TARGET_USE_FPU" |
| "trncf.dl %1, %0" |
| [(set_attr "length" "4") |
| (set_attr "type" "fpu")]) |
| |
| (define_insn "fixuns_truncdfdi2" |
| [(set (match_operand:DI 0 "register_operand" "=r") |
| (unsigned_fix:DI (match_operand:DF 1 "even_reg_operand" "r")))] |
| "TARGET_USE_FPU" |
| "trncf.dul %1, %0" |
| [(set_attr "length" "4") |
| (set_attr "type" "fpu")]) |
| |
| ;; int -> float |
| (define_insn "floatsisf2" |
| [(set (match_operand:SF 0 "register_operand" "=r") |
| (float:SF (match_operand:SI 1 "reg_or_0_operand" "rI")))] |
| "TARGET_USE_FPU" |
| "cvtf.ws %z1, %0" |
| [(set_attr "length" "4") |
| (set_attr "type" "fpu")]) |
| |
| (define_insn "unsfloatsisf2" |
| [(set (match_operand:SF 0 "register_operand" "=r") |
| (unsigned_float:SF (match_operand:SI 1 "reg_or_0_operand" "rI")))] |
| "TARGET_USE_FPU" |
| "cvtf.uws %z1, %0" |
| [(set_attr "length" "4") |
| (set_attr "type" "fpu")]) |
| |
| (define_insn "floatsidf2" |
| [(set (match_operand:DF 0 "even_reg_operand" "=r") |
| (float:DF (match_operand:SI 1 "reg_or_0_operand" "rI")))] |
| "TARGET_USE_FPU" |
| "cvtf.wd %z1,%0" |
| [(set_attr "length" "4") |
| (set_attr "type" "fpu")]) |
| |
| (define_insn "unsfloatsidf2" |
| [(set (match_operand:DF 0 "even_reg_operand" "=r") |
| (unsigned_float:DF (match_operand:SI 1 "reg_or_0_operand" "rI")))] |
| "TARGET_USE_FPU" |
| "cvtf.uwd %z1, %0" |
| [(set_attr "length" "4") |
| (set_attr "type" "fpu")]) |
| |
| (define_insn "floatdisf2" |
| [(set (match_operand:SF 0 "even_reg_operand" "=r") |
| (float:SF (match_operand:DI 1 "reg_or_0_operand" "rI")))] |
| "TARGET_USE_FPU" |
| "cvtf.ls %z1, %0" |
| [(set_attr "length" "4") |
| (set_attr "type" "fpu")]) |
| |
| (define_insn "unsfloatdisf2" |
| [(set (match_operand:SF 0 "even_reg_operand" "=r") |
| (unsigned_float:SF (match_operand:DI 1 "reg_or_0_operand" "rI")))] |
| "TARGET_USE_FPU" |
| "cvtf.uls %z1, %0" |
| [(set_attr "length" "4") |
| (set_attr "type" "fpu")]) |
| |
| (define_insn "floatdidf2" |
| [(set (match_operand:DF 0 "even_reg_operand" "=r") |
| (float:DF (match_operand:DI 1 "reg_or_0_operand" "rI")))] |
| "TARGET_USE_FPU" |
| "cvtf.ld %z1, %0" |
| [(set_attr "length" "4") |
| (set_attr "type" "fpu")]) |
| |
| (define_insn "unsfloatdidf2" |
| [(set (match_operand:DF 0 "even_reg_operand" "=r") |
| (unsigned_float:DF (match_operand:DI 1 "reg_or_0_operand" "rI")))] |
| "TARGET_USE_FPU" |
| "cvtf.uld %z1, %0" |
| [(set_attr "length" "4") |
| (set_attr "type" "fpu")]) |
| |
| ;; single-float -> double-float |
| (define_insn "extendsfdf2" |
| [(set (match_operand:DF 0 "even_reg_operand" "=r") |
| (float_extend:DF |
| (match_operand:SF 1 "reg_or_0_operand" "rI")))] |
| "TARGET_USE_FPU" |
| "cvtf.sd %z1,%0" |
| [(set_attr "length" "4") |
| (set_attr "type" "fpu")]) |
| |
| ;; double-float -> single-float |
| (define_insn "truncdfsf2" |
| [(set (match_operand:SF 0 "register_operand" "=r") |
| (float_truncate:SF |
| (match_operand:DF 1 "even_reg_operand" "r")))] |
| "TARGET_USE_FPU" |
| "cvtf.ds %1,%0" |
| [(set_attr "length" "4") |
| (set_attr "type" "fpu")]) |
| |
| ;; |
| ;; ---------------- special insns |
| ;; |
| |
| ;; reciprocal |
| |
| ;; Generic code demands that the recip and rsqrt named patterns |
| ;; have precisely one operand. So that's what we expose in the |
| ;; expander via the strange UNSPEC. However, those expanders |
| ;; generate normal looking recip and rsqrt patterns. |
| |
| (define_expand "recipsf2" |
| [(set (match_operand:SF 0 "register_operand" "") |
| (unspec:SF [(match_operand:SF 1 "register_operand" "")] |
| UNSPEC_RCP))] |
| "TARGET_USE_FPU" |
| { |
| emit_insn (gen_recipsf2_insn (operands[0], CONST1_RTX (SFmode), operands[1])); |
| DONE; |
| }) |
| |
| (define_insn "recipsf2_insn" |
| [(set (match_operand:SF 0 "register_operand" "=r") |
| (div:SF (match_operand:SF 1 "const_float_1_operand" "") |
| (match_operand:SF 2 "register_operand" "r")))] |
| "TARGET_USE_FPU" |
| "recipf.s %2,%0" |
| [(set_attr "length" "4") |
| (set_attr "type" "fpu")]) |
| |
| (define_expand "recipdf2" |
| [(set (match_operand:DF 0 "even_reg_operand" "") |
| (unspec:DF [(match_operand:SF 1 "even_reg_operand" "")] |
| UNSPEC_RCP))] |
| "TARGET_USE_FPU" |
| { |
| emit_insn (gen_recipdf2_insn (operands[0], CONST1_RTX (DFmode), operands[1])); |
| DONE; |
| }) |
| |
| (define_insn "recipdf2_insn" |
| [(set (match_operand:DF 0 "even_reg_operand" "=r") |
| (div:DF (match_operand:DF 1 "const_float_1_operand" "") |
| (match_operand:DF 2 "even_reg_operand" "r")))] |
| "TARGET_USE_FPU" |
| "recipf.d %2,%0" |
| [(set_attr "length" "4") |
| (set_attr "type" "fpu")]) |
| |
| ;;; reciprocal of square-root |
| (define_expand "rsqrtsf2" |
| [(set (match_operand:SF 0 "register_operand" "=") |
| (unspec:SF [(match_operand:SF 1 "register_operand" "")] |
| UNSPEC_RSQRT))] |
| "TARGET_USE_FPU" |
| { |
| emit_insn (gen_rsqrtsf2_insn (operands[0], CONST1_RTX (SFmode), operands[1])); |
| DONE; |
| }) |
| |
| (define_insn "rsqrtsf2_insn" |
| [(set (match_operand:SF 0 "register_operand" "=r") |
| (div:SF (match_operand:SF 1 "const_float_1_operand" "") |
| (sqrt:SF (match_operand:SF 2 "register_operand" "r"))))] |
| "TARGET_USE_FPU" |
| "rsqrtf.s %2,%0" |
| [(set_attr "length" "4") |
| (set_attr "type" "fpu")]) |
| |
| (define_expand "rsqrtdf2" |
| [(set (match_operand:DF 0 "even_reg_operand" "=r") |
| (unspec:DF [(match_operand:DF 1 "even_reg_operand" "r")] |
| UNSPEC_RSQRT))] |
| "TARGET_USE_FPU" |
| { |
| emit_insn (gen_rsqrtdf2_insn (operands[0], CONST1_RTX (DFmode), operands[1])); |
| DONE; |
| }) |
| |
| (define_insn "rsqrtdf2_insn" |
| [(set (match_operand:DF 0 "even_reg_operand" "=r") |
| (div:DF (match_operand:DF 1 "const_float_1_operand" "") |
| (sqrt:DF (match_operand:DF 2 "even_reg_operand" "r"))))] |
| "TARGET_USE_FPU" |
| "rsqrtf.d %2,%0" |
| [(set_attr "length" "4") |
| (set_attr "type" "fpu")]) |
| |
| ;; Note: The FPU-2.0 (ie pre e3v5) versions of these routines do not actually |
| ;; need operand 4 to be the same as operand 0. But the FPU-2.0 versions are |
| ;; also deprecated so the loss of flexibility is unimportant. |
| |
| ;;; multiply-add |
| (define_insn "fmasf4" |
| [(set (match_operand:SF 0 "register_operand" "=r") |
| (fma:SF (match_operand:SF 1 "register_operand" "r") |
| (match_operand:SF 2 "register_operand" "r") |
| (match_operand:SF 3 "register_operand" "0")))] |
| "TARGET_USE_FPU" |
| { return TARGET_V850E3V5_UP ? "fmaf.s %1, %2, %0" : "maddf.s %2, %1, %3, %0"; } |
| [(set_attr "length" "4") |
| (set_attr "type" "fpu")]) |
| |
| ;;; multiply-subtract |
| (define_insn "fmssf4" |
| [(set (match_operand:SF 0 "register_operand" "=r") |
| (fma:SF (match_operand:SF 1 "register_operand" "r") |
| (match_operand:SF 2 "register_operand" "r") |
| (neg:SF (match_operand:SF 3 "register_operand" "0"))))] |
| "TARGET_USE_FPU" |
| { return TARGET_V850E3V5_UP ? "fmsf.s %1, %2, %0" : "msubf.s %2, %1, %3, %0"; } |
| [(set_attr "length" "4") |
| (set_attr "type" "fpu")]) |
| |
| ;;; negative-multiply-add |
| (define_insn "fnmasf4" |
| [(set (match_operand:SF 0 "register_operand" "=r") |
| (neg:SF (fma:SF (match_operand:SF 1 "register_operand" "r") |
| (match_operand:SF 2 "register_operand" "r") |
| (match_operand:SF 3 "register_operand" "0"))))] |
| "TARGET_USE_FPU" |
| { return TARGET_V850E3V5_UP ? "fnmaf.s %1, %2, %0" : "nmaddf.s %2, %1, %3, %0"; } |
| [(set_attr "length" "4") |
| (set_attr "type" "fpu")]) |
| |
| ;; negative-multiply-subtract |
| (define_insn "fnmssf4" |
| [(set (match_operand:SF 0 "register_operand" "=r") |
| (neg:SF (fma:SF (match_operand:SF 1 "register_operand" "r") |
| (match_operand:SF 2 "register_operand" "r") |
| (neg:SF (match_operand:SF 3 "register_operand" "0")))))] |
| "TARGET_USE_FPU" |
| { return TARGET_V850E3V5_UP ? "fnmsf.s %1, %2, %0" : "nmsubf.s %2, %1, %3, %0"; } |
| [(set_attr "length" "4") |
| (set_attr "type" "fpu")]) |
| ; |
| ; ---------------- comparison/conditionals |
| ; |
| ; SF |
| |
| (define_insn "cmpsf_le_insn" |
| [(set (reg:CC_FPU_LE FCC_REGNUM) |
| (compare:CC_FPU_LE (match_operand:SF 0 "register_operand" "r") |
| (match_operand:SF 1 "register_operand" "r")))] |
| "reload_completed && TARGET_USE_FPU" |
| "cmpf.s le, %z0, %z1" |
| [(set_attr "length" "4") |
| (set_attr "type" "fpu")]) |
| |
| (define_insn "cmpsf_lt_insn" |
| [(set (reg:CC_FPU_LT FCC_REGNUM) |
| (compare:CC_FPU_LT (match_operand:SF 0 "register_operand" "r") |
| (match_operand:SF 1 "register_operand" "r")))] |
| "reload_completed && TARGET_USE_FPU" |
| "cmpf.s lt, %z0, %z1" |
| [(set_attr "length" "4") |
| (set_attr "type" "fpu")]) |
| |
| (define_insn "cmpsf_ge_insn" |
| [(set (reg:CC_FPU_GE FCC_REGNUM) |
| (compare:CC_FPU_GE (match_operand:SF 0 "register_operand" "r") |
| (match_operand:SF 1 "register_operand" "r")))] |
| "reload_completed && TARGET_USE_FPU" |
| "cmpf.s le, %z1, %z0" |
| [(set_attr "length" "4") |
| (set_attr "type" "fpu")]) |
| |
| (define_insn "cmpsf_gt_insn" |
| [(set (reg:CC_FPU_GT FCC_REGNUM) |
| (compare:CC_FPU_GT (match_operand:SF 0 "register_operand" "r") |
| (match_operand:SF 1 "register_operand" "r")))] |
| "reload_completed && TARGET_USE_FPU" |
| "cmpf.s lt, %z1, %z0" |
| [(set_attr "length" "4") |
| (set_attr "type" "fpu")]) |
| |
| (define_insn "cmpsf_eq_insn" |
| [(set (reg:CC_FPU_EQ FCC_REGNUM) |
| (compare:CC_FPU_EQ (match_operand:SF 0 "register_operand" "r") |
| (match_operand:SF 1 "register_operand" "r")))] |
| "reload_completed && TARGET_USE_FPU" |
| "cmpf.s eq, %z0, %z1" |
| [(set_attr "length" "4") |
| (set_attr "type" "fpu")]) |
| |
| ; DF |
| |
| (define_insn "cmpdf_le_insn" |
| [(set (reg:CC_FPU_LE FCC_REGNUM) |
| (compare:CC_FPU_LE (match_operand:DF 0 "even_reg_operand" "r") |
| (match_operand:DF 1 "even_reg_operand" "r")))] |
| "reload_completed && TARGET_USE_FPU" |
| "cmpf.d le, %z0, %z1" |
| [(set_attr "length" "4") |
| (set_attr "type" "fpu")]) |
| |
| (define_insn "cmpdf_lt_insn" |
| [(set (reg:CC_FPU_LT FCC_REGNUM) |
| (compare:CC_FPU_LT (match_operand:DF 0 "even_reg_operand" "r") |
| (match_operand:DF 1 "even_reg_operand" "r")))] |
| "reload_completed && TARGET_USE_FPU" |
| "cmpf.d lt, %z0, %z1" |
| [(set_attr "length" "4") |
| (set_attr "type" "fpu")]) |
| |
| (define_insn "cmpdf_ge_insn" |
| [(set (reg:CC_FPU_GE FCC_REGNUM) |
| (compare:CC_FPU_GE (match_operand:DF 0 "even_reg_operand" "r") |
| (match_operand:DF 1 "even_reg_operand" "r")))] |
| "reload_completed && TARGET_USE_FPU" |
| "cmpf.d le, %z1, %z0" |
| [(set_attr "length" "4") |
| (set_attr "type" "fpu")]) |
| |
| (define_insn "cmpdf_gt_insn" |
| [(set (reg:CC_FPU_GT FCC_REGNUM) |
| (compare:CC_FPU_GT (match_operand:DF 0 "even_reg_operand" "r") |
| (match_operand:DF 1 "even_reg_operand" "r")))] |
| "reload_completed && TARGET_USE_FPU" |
| "cmpf.d lt, %z1, %z0" |
| [(set_attr "length" "4") |
| (set_attr "type" "fpu")]) |
| |
| (define_insn "cmpdf_eq_insn" |
| [(set (reg:CC_FPU_EQ FCC_REGNUM) |
| (compare:CC_FPU_EQ (match_operand:DF 0 "even_reg_operand" "r") |
| (match_operand:DF 1 "even_reg_operand" "r")))] |
| "reload_completed && TARGET_USE_FPU" |
| "cmpf.d eq, %z0, %z1" |
| [(set_attr "length" "4") |
| (set_attr "type" "fpu")]) |
| |
| ;; |
| ;; Transfer a v850e2v3 fcc to the Z bit of CC0 (this is necessary to do a |
| ;; conditional branch based on a floating-point compare) |
| ;; |
| |
| (define_insn "trfsr" |
| [(set (match_operand 0 "" "") (match_operand 1 "" ""))] |
| "reload_completed |
| && TARGET_USE_FPU |
| && GET_MODE(operands[0]) == GET_MODE(operands[1]) |
| && GET_CODE(operands[0]) == REG && REGNO (operands[0]) == CC_REGNUM |
| && GET_CODE(operands[1]) == REG && REGNO (operands[1]) == FCC_REGNUM |
| && (GET_MODE(operands[0]) == CC_FPU_LEmode |
| || GET_MODE(operands[0]) == CC_FPU_GEmode |
| || GET_MODE(operands[0]) == CC_FPU_LTmode |
| || GET_MODE(operands[0]) == CC_FPU_GTmode |
| || GET_MODE(operands[0]) == CC_FPU_EQmode |
| || GET_MODE(operands[0]) == CC_FPU_NEmode)" |
| "trfsr" |
| [(set_attr "length" "4") |
| (set_attr "type" "fpu")]) |
| |
| ;; |
| ;; Floating-point conditional moves for the v850e2v3. |
| ;; |
| |
| ;; The actual v850e2v3 conditional move instructions |
| ;; |
| (define_insn "movsfcc_z_insn" |
| [(set (match_operand:SF 0 "register_operand" "=r") |
| (if_then_else:SF |
| (match_operand 3 "v850_float_z_comparison_operator" "") |
| (match_operand:SF 1 "reg_or_0_operand" "rIG") |
| (match_operand:SF 2 "reg_or_0_operand" "rIG")))] |
| "TARGET_USE_FPU" |
| "cmovf.s 0,%z1,%z2,%0") |
| |
| (define_insn "movsfcc_nz_insn" |
| [(set (match_operand:SF 0 "register_operand" "=r") |
| (if_then_else:SF |
| (match_operand 3 "v850_float_nz_comparison_operator" "") |
| (match_operand:SF 1 "reg_or_0_operand" "rIG") |
| (match_operand:SF 2 "reg_or_0_operand" "rIG")))] |
| "TARGET_USE_FPU" |
| "cmovf.s 0,%z2,%z1,%0") |
| |
| (define_insn "movdfcc_z_insn" |
| [(set (match_operand:DF 0 "even_reg_operand" "=r") |
| (if_then_else:DF |
| (match_operand 3 "v850_float_z_comparison_operator" "") |
| (match_operand:DF 1 "even_reg_operand" "r") |
| (match_operand:DF 2 "even_reg_operand" "r")))] |
| "TARGET_USE_FPU" |
| "cmovf.d 0,%z1,%z2,%0") |
| |
| (define_insn "movdfcc_nz_insn" |
| [(set (match_operand:DF 0 "even_reg_operand" "=r") |
| (if_then_else:DF |
| (match_operand 3 "v850_float_nz_comparison_operator" "") |
| (match_operand:DF 1 "even_reg_operand" "r") |
| (match_operand:DF 2 "even_reg_operand" "r")))] |
| "TARGET_USE_FPU" |
| "cmovf.d 0,%z2,%z1,%0") |
| |
| (define_insn "movedfcc_z_zero" |
| [(set (match_operand:DF 0 "register_operand" "=r") |
| (if_then_else:DF |
| (match_operand 3 "v850_float_z_comparison_operator" "") |
| (match_operand:DF 1 "reg_or_0_operand" "rIG") |
| (match_operand:DF 2 "reg_or_0_operand" "rIG")))] |
| "TARGET_USE_FPU" |
| "cmovf.s 0,%z1,%z2,%0 ; cmovf.s 0,%Z1,%Z2,%R0" |
| [(set_attr "length" "8")]) |
| |
| (define_insn "movedfcc_nz_zero" |
| [(set (match_operand:DF 0 "register_operand" "=r") |
| (if_then_else:DF |
| (match_operand 3 "v850_float_nz_comparison_operator" "") |
| (match_operand:DF 1 "reg_or_0_operand" "rIG") |
| (match_operand:DF 2 "reg_or_0_operand" "rIG")))] |
| "TARGET_USE_FPU" |
| "cmovf.s 0,%z2,%z1,%0 ; cmovf.s 0,%Z2,%Z1,%R0" |
| [(set_attr "length" "8")]) |
| |
| |
| ;; ---------------------------------------------------------------------- |
| ;; HELPER INSTRUCTIONS for saving the prologue and epilogue registers |
| ;; ---------------------------------------------------------------------- |
| |
| ;; This pattern will match a stack adjust RTX followed by any number of push |
| ;; RTXs. These RTXs will then be turned into a suitable call to a worker |
| ;; function. |
| |
| ;; |
| ;; Actually, convert the RTXs into a PREPARE instruction. |
| ;; |
| |
| (define_insn "" |
| [(match_parallel 0 "pattern_is_ok_for_prepare" |
| [(set (reg:SI 3) |
| (plus:SI (reg:SI 3) (match_operand:SI 1 "immediate_operand" "i"))) |
| (set (mem:SI (plus:SI (reg:SI 3) |
| (match_operand:SI 2 "immediate_operand" "i"))) |
| (match_operand:SI 3 "register_is_ok_for_epilogue" "r"))])] |
| "TARGET_PROLOG_FUNCTION && (TARGET_V850E_UP)" |
| { |
| return construct_prepare_instruction (operands[0]); |
| } |
| [(set_attr "length" "4")]) |
| |
| (define_insn "" |
| [(match_parallel 0 "pattern_is_ok_for_prologue" |
| [(set (reg:SI 3) |
| (plus:SI (reg:SI 3) (match_operand:SI 1 "immediate_operand" "i"))) |
| (set (mem:SI (plus:SI (reg:SI 3) |
| (match_operand:SI 2 "immediate_operand" "i"))) |
| (match_operand:SI 3 "register_is_ok_for_epilogue" "r"))])] |
| "TARGET_PROLOG_FUNCTION" |
| { |
| return construct_save_jarl (operands[0]); |
| } |
| [(set (attr "length") (if_then_else (eq_attr "long_calls" "yes") |
| (const_string "16") |
| (const_string "4")))]) |
| |
| ;; |
| ;; Actually, turn the RTXs into a DISPOSE instruction. |
| ;; |
| (define_insn "" |
| [(match_parallel 0 "pattern_is_ok_for_dispose" |
| [(return) |
| (set (reg:SI 3) |
| (plus:SI (reg:SI 3) (match_operand:SI 1 "immediate_operand" "i"))) |
| (set (match_operand:SI 2 "register_is_ok_for_epilogue" "=r") |
| (mem:SI (plus:SI (reg:SI 3) |
| (match_operand:SI 3 "immediate_operand" "i"))))])] |
| "TARGET_PROLOG_FUNCTION && (TARGET_V850E_UP)" |
| { |
| return construct_dispose_instruction (operands[0]); |
| } |
| [(set_attr "length" "4")]) |
| |
| ;; This pattern will match a return RTX followed by any number of pop RTXs |
| ;; and possible a stack adjustment as well. These RTXs will be turned into |
| ;; a suitable call to a worker function. |
| |
| (define_insn "" |
| [(match_parallel 0 "pattern_is_ok_for_epilogue" |
| [(return) |
| (set (reg:SI 3) |
| (plus:SI (reg:SI 3) (match_operand:SI 1 "immediate_operand" "i"))) |
| (set (match_operand:SI 2 "register_is_ok_for_epilogue" "=r") |
| (mem:SI (plus:SI (reg:SI 3) |
| (match_operand:SI 3 "immediate_operand" "i"))))])] |
| "TARGET_PROLOG_FUNCTION" |
| { |
| return construct_restore_jr (operands[0]); |
| } |
| [(set (attr "length") (if_then_else (eq_attr "long_calls" "yes") |
| (const_string "12") |
| (const_string "4")))]) |
| |
| ;; Initialize an interrupt function. Do not depend on TARGET_PROLOG_FUNCTION. |
| (define_insn "callt_save_interrupt" |
| [(unspec_volatile [(const_int 0)] 2) |
| (clobber (reg:CC CC_REGNUM))] |
| "(TARGET_V850E_UP) && !TARGET_DISABLE_CALLT" |
| ;; The CALLT instruction stores the next address of CALLT to CTPC register |
| ;; without saving its previous value. So if the interrupt handler |
| ;; or its caller could possibly execute the CALLT insn, save_interrupt |
| ;; MUST NOT be called via CALLT. |
| { |
| output_asm_insn ("addi -28, sp, sp", operands); |
| output_asm_insn ("st.w r1, 24[sp]", operands); |
| output_asm_insn ("st.w r10, 12[sp]", operands); |
| output_asm_insn ("st.w r11, 16[sp]", operands); |
| output_asm_insn ("stsr ctpc, r10", operands); |
| output_asm_insn ("st.w r10, 20[sp]", operands); |
| output_asm_insn ("stsr ctpsw, r10", operands); |
| output_asm_insn ("st.w r10, 24[sp]", operands); |
| output_asm_insn ("callt ctoff(__callt_save_interrupt)", operands); |
| return ""; |
| } |
| [(set_attr "length" "26")]) |
| |
| (define_insn "callt_return_interrupt" |
| [(unspec_volatile [(const_int 0)] 3) |
| (clobber (reg:CC CC_REGNUM))] |
| "(TARGET_V850E_UP) && !TARGET_DISABLE_CALLT" |
| "callt ctoff(__callt_return_interrupt)" |
| [(set_attr "length" "2")]) |
| |
| (define_insn "save_interrupt" |
| [(set (reg:SI 3) (plus:SI (reg:SI 3) (const_int -20))) |
| (set (mem:SI (plus:SI (reg:SI 3) (const_int -20))) (reg:SI 30)) |
| (set (mem:SI (plus:SI (reg:SI 3) (const_int -16))) (reg:SI 4)) |
| (set (mem:SI (plus:SI (reg:SI 3) (const_int -12))) (reg:SI 1)) |
| (set (mem:SI (plus:SI (reg:SI 3) (const_int -8))) (reg:SI 10)) |
| (set (mem:SI (plus:SI (reg:SI 3) (const_int -4))) (reg:SI 11)) |
| (clobber (reg:CC CC_REGNUM))] |
| "" |
| { |
| if (TARGET_PROLOG_FUNCTION && !TARGET_LONG_CALLS) |
| return "addi -20,sp,sp \; st.w r11,16[sp] \; st.w r10,12[sp] \; jarl __save_interrupt,r10"; |
| else |
| { |
| output_asm_insn ("addi -20, sp, sp", operands); |
| output_asm_insn ("st.w r11, 16[sp]", operands); |
| output_asm_insn ("st.w r10, 12[sp]", operands); |
| output_asm_insn ("st.w ep, 0[sp]", operands); |
| output_asm_insn ("st.w gp, 4[sp]", operands); |
| output_asm_insn ("st.w r1, 8[sp]", operands); |
| output_asm_insn ("movhi hi(__ep), r0, ep", operands); |
| output_asm_insn ("movea lo(__ep), ep, ep", operands); |
| output_asm_insn ("movhi hi(__gp), r0, gp", operands); |
| output_asm_insn ("movea lo(__gp), gp, gp", operands); |
| return ""; |
| } |
| } |
| [(set (attr "length") |
| (if_then_else (match_test "TARGET_LONG_CALLS") |
| (const_int 10) |
| (const_int 34)))]) |
| |
| ;; Restore r1, r4, r10, and return from the interrupt |
| (define_insn "return_interrupt" |
| [(return) |
| (set (reg:SI 3) (plus:SI (reg:SI 3) (const_int 20))) |
| (set (reg:SI 11) (mem:SI (plus:SI (reg:SI 3) (const_int 16)))) |
| (set (reg:SI 10) (mem:SI (plus:SI (reg:SI 3) (const_int 12)))) |
| (set (reg:SI 1) (mem:SI (plus:SI (reg:SI 3) (const_int 8)))) |
| (set (reg:SI 4) (mem:SI (plus:SI (reg:SI 3) (const_int 4)))) |
| (set (reg:SI 30) (mem:SI (reg:SI 3))) |
| (clobber (reg:CC CC_REGNUM))] |
| "" |
| { |
| if (TARGET_PROLOG_FUNCTION && !TARGET_LONG_CALLS) |
| return "jr __return_interrupt"; |
| else |
| { |
| output_asm_insn ("ld.w 0[sp], ep", operands); |
| output_asm_insn ("ld.w 4[sp], gp", operands); |
| output_asm_insn ("ld.w 8[sp], r1", operands); |
| output_asm_insn ("ld.w 12[sp], r10", operands); |
| output_asm_insn ("ld.w 16[sp], r11", operands); |
| output_asm_insn ("addi 20, sp, sp", operands); |
| output_asm_insn ("reti", operands); |
| return ""; |
| } |
| } |
| [(set (attr "length") |
| (if_then_else (match_test "TARGET_LONG_CALLS") |
| (const_int 4) |
| (const_int 24)))]) |
| |
| ;; Save all registers except for the registers saved in save_interrupt when |
| ;; an interrupt function makes a call. |
| ;; UNSPEC_VOLATILE is considered to use and clobber all hard registers and |
| ;; all of memory. This blocks insns from being moved across this point. |
| ;; This is needed because the rest of the compiler is not ready to handle |
| ;; insns this complicated. |
| |
| (define_insn "callt_save_all_interrupt" |
| [(unspec_volatile [(const_int 0)] 0) |
| (clobber (reg:CC CC_REGNUM))] |
| "(TARGET_V850E_UP) && !TARGET_DISABLE_CALLT" |
| "callt ctoff(__callt_save_all_interrupt)" |
| [(set_attr "length" "2")]) |
| |
| (define_insn "save_all_interrupt" |
| [(unspec_volatile [(const_int 0)] 0) |
| (clobber (reg:CC CC_REGNUM))] |
| "" |
| { |
| if (TARGET_PROLOG_FUNCTION && !TARGET_LONG_CALLS) |
| return "jarl __save_all_interrupt,r10"; |
| |
| output_asm_insn ("addi -120, sp, sp", operands); |
| |
| if (TARGET_EP) |
| { |
| output_asm_insn ("mov ep, r1", operands); |
| output_asm_insn ("mov sp, ep", operands); |
| output_asm_insn ("sst.w r31, 116[ep]", operands); |
| output_asm_insn ("sst.w r2, 112[ep]", operands); |
| output_asm_insn ("sst.w gp, 108[ep]", operands); |
| output_asm_insn ("sst.w r6, 104[ep]", operands); |
| output_asm_insn ("sst.w r7, 100[ep]", operands); |
| output_asm_insn ("sst.w r8, 96[ep]", operands); |
| output_asm_insn ("sst.w r9, 92[ep]", operands); |
| output_asm_insn ("sst.w r11, 88[ep]", operands); |
| output_asm_insn ("sst.w r12, 84[ep]", operands); |
| output_asm_insn ("sst.w r13, 80[ep]", operands); |
| output_asm_insn ("sst.w r14, 76[ep]", operands); |
| output_asm_insn ("sst.w r15, 72[ep]", operands); |
| output_asm_insn ("sst.w r16, 68[ep]", operands); |
| output_asm_insn ("sst.w r17, 64[ep]", operands); |
| output_asm_insn ("sst.w r18, 60[ep]", operands); |
| output_asm_insn ("sst.w r19, 56[ep]", operands); |
| output_asm_insn ("sst.w r20, 52[ep]", operands); |
| output_asm_insn ("sst.w r21, 48[ep]", operands); |
| output_asm_insn ("sst.w r22, 44[ep]", operands); |
| output_asm_insn ("sst.w r23, 40[ep]", operands); |
| output_asm_insn ("sst.w r24, 36[ep]", operands); |
| output_asm_insn ("sst.w r25, 32[ep]", operands); |
| output_asm_insn ("sst.w r26, 28[ep]", operands); |
| output_asm_insn ("sst.w r27, 24[ep]", operands); |
| output_asm_insn ("sst.w r28, 20[ep]", operands); |
| output_asm_insn ("sst.w r29, 16[ep]", operands); |
| output_asm_insn ("mov r1, ep", operands); |
| } |
| else |
| { |
| output_asm_insn ("st.w r31, 116[sp]", operands); |
| output_asm_insn ("st.w r2, 112[sp]", operands); |
| output_asm_insn ("st.w gp, 108[sp]", operands); |
| output_asm_insn ("st.w r6, 104[sp]", operands); |
| output_asm_insn ("st.w r7, 100[sp]", operands); |
| output_asm_insn ("st.w r8, 96[sp]", operands); |
| output_asm_insn ("st.w r9, 92[sp]", operands); |
| output_asm_insn ("st.w r11, 88[sp]", operands); |
| output_asm_insn ("st.w r12, 84[sp]", operands); |
| output_asm_insn ("st.w r13, 80[sp]", operands); |
| output_asm_insn ("st.w r14, 76[sp]", operands); |
| output_asm_insn ("st.w r15, 72[sp]", operands); |
| output_asm_insn ("st.w r16, 68[sp]", operands); |
| output_asm_insn ("st.w r17, 64[sp]", operands); |
| output_asm_insn ("st.w r18, 60[sp]", operands); |
| output_asm_insn ("st.w r19, 56[sp]", operands); |
| output_asm_insn ("st.w r20, 52[sp]", operands); |
| output_asm_insn ("st.w r21, 48[sp]", operands); |
| output_asm_insn ("st.w r22, 44[sp]", operands); |
| output_asm_insn ("st.w r23, 40[sp]", operands); |
| output_asm_insn ("st.w r24, 36[sp]", operands); |
| output_asm_insn ("st.w r25, 32[sp]", operands); |
| output_asm_insn ("st.w r26, 28[sp]", operands); |
| output_asm_insn ("st.w r27, 24[sp]", operands); |
| output_asm_insn ("st.w r28, 20[sp]", operands); |
| output_asm_insn ("st.w r29, 16[sp]", operands); |
| } |
| |
| return ""; |
| } |
| [(set (attr "length") |
| (if_then_else (match_test "TARGET_LONG_CALLS") |
| (const_int 4) |
| (const_int 62) |
| ))]) |
| |
| (define_insn "_save_all_interrupt" |
| [(unspec_volatile [(const_int 0)] 0) |
| (clobber (reg:CC CC_REGNUM))] |
| "TARGET_V850 && ! TARGET_LONG_CALLS" |
| "jarl __save_all_interrupt,r10" |
| [(set_attr "length" "4")]) |
| |
| ;; Restore all registers saved when an interrupt function makes a call. |
| ;; UNSPEC_VOLATILE is considered to use and clobber all hard registers and |
| ;; all of memory. This blocks insns from being moved across this point. |
| ;; This is needed because the rest of the compiler is not ready to handle |
| ;; insns this complicated. |
| |
| (define_insn "callt_restore_all_interrupt" |
| [(unspec_volatile [(const_int 0)] 1) |
| (clobber (reg:CC CC_REGNUM))] |
| "(TARGET_V850E_UP) && !TARGET_DISABLE_CALLT" |
| "callt ctoff(__callt_restore_all_interrupt)" |
| [(set_attr "length" "2")]) |
| |
| (define_insn "restore_all_interrupt" |
| [(unspec_volatile [(const_int 0)] 1) |
| (clobber (reg:CC CC_REGNUM))] |
| "" |
| { |
| if (TARGET_PROLOG_FUNCTION && !TARGET_LONG_CALLS) |
| return "jarl __restore_all_interrupt,r10"; |
| |
| if (TARGET_EP) |
| { |
| output_asm_insn ("mov ep, r1", operands); |
| output_asm_insn ("mov sp, ep", operands); |
| output_asm_insn ("sld.w 116[ep], r31", operands); |
| output_asm_insn ("sld.w 112[ep], r2", operands); |
| output_asm_insn ("sld.w 108[ep], gp", operands); |
| output_asm_insn ("sld.w 104[ep], r6", operands); |
| output_asm_insn ("sld.w 100[ep], r7", operands); |
| output_asm_insn ("sld.w 96[ep], r8", operands); |
| output_asm_insn ("sld.w 92[ep], r9", operands); |
| output_asm_insn ("sld.w 88[ep], r11", operands); |
| output_asm_insn ("sld.w 84[ep], r12", operands); |
| output_asm_insn ("sld.w 80[ep], r13", operands); |
| output_asm_insn ("sld.w 76[ep], r14", operands); |
| output_asm_insn ("sld.w 72[ep], r15", operands); |
| output_asm_insn ("sld.w 68[ep], r16", operands); |
| output_asm_insn ("sld.w 64[ep], r17", operands); |
| output_asm_insn ("sld.w 60[ep], r18", operands); |
| output_asm_insn ("sld.w 56[ep], r19", operands); |
| output_asm_insn ("sld.w 52[ep], r20", operands); |
| output_asm_insn ("sld.w 48[ep], r21", operands); |
| output_asm_insn ("sld.w 44[ep], r22", operands); |
| output_asm_insn ("sld.w 40[ep], r23", operands); |
| output_asm_insn ("sld.w 36[ep], r24", operands); |
| output_asm_insn ("sld.w 32[ep], r25", operands); |
| output_asm_insn ("sld.w 28[ep], r26", operands); |
| output_asm_insn ("sld.w 24[ep], r27", operands); |
| output_asm_insn ("sld.w 20[ep], r28", operands); |
| output_asm_insn ("sld.w 16[ep], r29", operands); |
| output_asm_insn ("mov r1, ep", operands); |
| } |
| else |
| { |
| output_asm_insn ("ld.w 116[sp], r31", operands); |
| output_asm_insn ("ld.w 112[sp], r2", operands); |
| output_asm_insn ("ld.w 108[sp], gp", operands); |
| output_asm_insn ("ld.w 104[sp], r6", operands); |
| output_asm_insn ("ld.w 100[sp], r7", operands); |
| output_asm_insn ("ld.w 96[sp], r8", operands); |
| output_asm_insn ("ld.w 92[sp], r9", operands); |
| output_asm_insn ("ld.w 88[sp], r11", operands); |
| output_asm_insn ("ld.w 84[sp], r12", operands); |
| output_asm_insn ("ld.w 80[sp], r13", operands); |
| output_asm_insn ("ld.w 76[sp], r14", operands); |
| output_asm_insn ("ld.w 72[sp], r15", operands); |
| output_asm_insn ("ld.w 68[sp], r16", operands); |
| output_asm_insn ("ld.w 64[sp], r17", operands); |
| output_asm_insn ("ld.w 60[sp], r18", operands); |
| output_asm_insn ("ld.w 56[sp], r19", operands); |
| output_asm_insn ("ld.w 52[sp], r20", operands); |
| output_asm_insn ("ld.w 48[sp], r21", operands); |
| output_asm_insn ("ld.w 44[sp], r22", operands); |
| output_asm_insn ("ld.w 40[sp], r23", operands); |
| output_asm_insn ("ld.w 36[sp], r24", operands); |
| output_asm_insn ("ld.w 32[sp], r25", operands); |
| output_asm_insn ("ld.w 28[sp], r26", operands); |
| output_asm_insn ("ld.w 24[sp], r27", operands); |
| output_asm_insn ("ld.w 20[sp], r28", operands); |
| output_asm_insn ("ld.w 16[sp], r29", operands); |
| } |
| output_asm_insn ("addi 120, sp, sp", operands); |
| return ""; |
| } |
| [(set (attr "length") |
| (if_then_else (match_test "TARGET_LONG_CALLS") |
| (const_int 4) |
| (const_int 62) |
| ))]) |
| |
| (define_insn "_restore_all_interrupt" |
| [(unspec_volatile [(const_int 0)] 1) |
| (clobber (reg:CC CC_REGNUM))] |
| "TARGET_V850 && ! TARGET_LONG_CALLS" |
| "jarl __restore_all_interrupt,r10" |
| [(set_attr "length" "4")]) |