From 1bc5aee63eb72b341f506ad058502cd0361f0d10 Mon Sep 17 00:00:00 2001 From: Ben Cheng Date: Tue, 25 Mar 2014 22:37:19 -0700 Subject: Initial checkin of GCC 4.9.0 from trunk (r208799). Change-Id: I48a3c08bb98542aa215912a75f03c0890e497dba --- gcc-4.9/gcc/config/m32r/m32r.md | 2276 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 2276 insertions(+) create mode 100644 gcc-4.9/gcc/config/m32r/m32r.md (limited to 'gcc-4.9/gcc/config/m32r/m32r.md') diff --git a/gcc-4.9/gcc/config/m32r/m32r.md b/gcc-4.9/gcc/config/m32r/m32r.md new file mode 100644 index 000000000..47efb910d --- /dev/null +++ b/gcc-4.9/gcc/config/m32r/m32r.md @@ -0,0 +1,2276 @@ +;; Machine description of the Renesas M32R cpu for GNU C compiler +;; Copyright (C) 1996-2014 Free Software Foundation, Inc. + +;; This file is part of GCC. + +;; GCC is free software; you can redistribute it and/or modify it +;; under the terms of the GNU General Public License as published +;; by the Free Software Foundation; either version 3, or (at your +;; option) any later version. + +;; GCC is distributed in the hope that it will be useful, but WITHOUT +;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +;; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +;; License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with GCC; see the file COPYING3. If not see +;; . + +;; See file "rtl.def" for documentation on define_insn, match_*, et. al. + +;; UNSPEC_VOLATILE usage +(define_constants + [(UNSPECV_BLOCKAGE 0) + (UNSPECV_FLUSH_ICACHE 1)]) + +;; UNSPEC usage +(define_constants + [(UNSPEC_LOAD_SDA_BASE 2) + (UNSPEC_SET_CBIT 3) + (UNSPEC_PIC_LOAD_ADDR 4) + (UNSPEC_GET_PC 5) + (UNSPEC_GOTOFF 6) + ]) + +;; Insn type. Used to default other attribute values. +(define_attr "type" + "int2,int4,load2,load4,load8,store2,store4,store8,shift2,shift4,mul2,div4,uncond_branch,branch,call,multi,misc" + (const_string "misc")) + +;; Length in bytes. +(define_attr "length" "" + (cond [(eq_attr "type" "int2,load2,store2,shift2,mul2") + (const_int 2) + + (eq_attr "type" "int4,load4,store4,shift4,div4") + (const_int 4) + + (eq_attr "type" "multi") + (const_int 8) + + (eq_attr "type" "uncond_branch,branch,call") + (const_int 4)] + + (const_int 4))) + +;; The length here is the length of a single asm. Unfortunately it might be +;; 2 or 4 so we must allow for 4. That's ok though. +(define_asm_attributes + [(set_attr "length" "4") + (set_attr "type" "multi")]) + +;; Whether an instruction is short (16-bit) or long (32-bit). +(define_attr "insn_size" "short,long" + (if_then_else (eq_attr "type" "int2,load2,store2,shift2,mul2") + (const_string "short") + (const_string "long"))) + +;; The target CPU we're compiling for. +(define_attr "cpu" "m32r,m32r2,m32rx" + (cond [(match_test "TARGET_M32RX") + (const_string "m32rx") + (match_test "TARGET_M32R2") + (const_string "m32r2")] + (const_string "m32r"))) + +;; Defines the pipeline where an instruction can be executed on. +;; For the M32R, a short instruction can execute one of the two pipes. +;; For the M32Rx, the restrictions are modelled in the second +;; condition of this attribute definition. +(define_attr "m32r_pipeline" "either,s,o,long" + (cond [(and (eq_attr "cpu" "m32r") + (eq_attr "insn_size" "short")) + (const_string "either") + (eq_attr "insn_size" "!short") + (const_string "long")] + (cond [(eq_attr "type" "int2") + (const_string "either") + (eq_attr "type" "load2,store2,shift2,uncond_branch,branch,call") + (const_string "o") + (eq_attr "type" "mul2") + (const_string "s")] + (const_string "long")))) + +;; :::::::::::::::::::: +;; :: +;; :: Pipeline description +;; :: +;; :::::::::::::::::::: + +;; This model is based on Chapter 2, Appendix 3 and Appendix 4 of the +;; "M32R-FPU Software Manual", Revision 1.01, plus additional information +;; obtained by our best friend and mine, Google. +;; +;; The pipeline is modelled as a fetch unit, and a core with a memory unit, +;; two execution units, where "fetch" models IF and D, "memory" for MEM1 +;; and MEM2, and "EXEC" for E, E1, E2, EM, and EA. Writeback and +;; bypasses are not modelled. +(define_automaton "m32r") + +;; We pretend there are two short (16 bits) instruction fetchers. The +;; "s" short fetcher cannot be reserved until the "o" short fetcher is +;; reserved. Some instructions reserve both the left and right fetchers. +;; These fetch units are a hack to get GCC to better pack the instructions +;; for the M32Rx processor, which has two execution pipes. +;; +;; In reality there is only one decoder, which can decode either two 16-bit +;; instructions, or a single 32-bit instruction. +;; +;; Note, "fetch" models both the IF and the D pipeline stages. +;; +;; The m32rx core has two execution pipes. We name them o_E and s_E. +;; In addition, there's a memory unit. + +(define_cpu_unit "o_IF,s_IF,o_E,s_E,memory" "m32r") + +;; Prevent the s pipe from being reserved before the o pipe. +(absence_set "s_IF" "o_IF") +(absence_set "s_E" "o_E") + +;; On the M32Rx, long instructions execute on both pipes, so reserve +;; both fetch slots and both pipes. +(define_reservation "long_IF" "o_IF+s_IF") +(define_reservation "long_E" "o_E+s_E") + +;; :::::::::::::::::::: + +;; Simple instructions do 4 stages: IF D E WB. WB is not modelled. +;; Hence, ready latency is 1. +(define_insn_reservation "short_left" 1 + (and (eq_attr "m32r_pipeline" "o") + (and (eq_attr "insn_size" "short") + (eq_attr "type" "!load2"))) + "o_IF,o_E") + +(define_insn_reservation "short_right" 1 + (and (eq_attr "m32r_pipeline" "s") + (and (eq_attr "insn_size" "short") + (eq_attr "type" "!load2"))) + "s_IF,s_E") + +(define_insn_reservation "short_either" 1 + (and (eq_attr "m32r_pipeline" "either") + (and (eq_attr "insn_size" "short") + (eq_attr "type" "!load2"))) + "o_IF|s_IF,o_E|s_E") + +(define_insn_reservation "long_m32r" 1 + (and (eq_attr "cpu" "m32r") + (and (eq_attr "insn_size" "long") + (eq_attr "type" "!load4,load8"))) + "long_IF,long_E") + +(define_insn_reservation "long_m32rx" 2 + (and (eq_attr "m32r_pipeline" "long") + (and (eq_attr "insn_size" "long") + (eq_attr "type" "!load4,load8"))) + "long_IF,long_E") + +;; Load/store instructions do 6 stages: IF D E MEM1 MEM2 WB. +;; MEM1 may require more than one cycle depending on locality. We +;; optimistically assume all memory is nearby, i.e. MEM1 takes only +;; one cycle. Hence, ready latency is 3. + +;; The M32Rx can do short load/store only on the left pipe. +(define_insn_reservation "short_load_left" 3 + (and (eq_attr "m32r_pipeline" "o") + (and (eq_attr "insn_size" "short") + (eq_attr "type" "load2"))) + "o_IF,o_E,memory*2") + +(define_insn_reservation "short_load" 3 + (and (eq_attr "m32r_pipeline" "either") + (and (eq_attr "insn_size" "short") + (eq_attr "type" "load2"))) + "s_IF|o_IF,s_E|o_E,memory*2") + +(define_insn_reservation "long_load" 3 + (and (eq_attr "cpu" "m32r") + (and (eq_attr "insn_size" "long") + (eq_attr "type" "load4,load8"))) + "long_IF,long_E,memory*2") + +(define_insn_reservation "long_load_m32rx" 3 + (and (eq_attr "m32r_pipeline" "long") + (eq_attr "type" "load4,load8")) + "long_IF,long_E,memory*2") + + +(include "predicates.md") +(include "constraints.md") + +;; Expand prologue as RTL +(define_expand "prologue" + [(const_int 1)] + "" + " +{ + m32r_expand_prologue (); + DONE; +}") + +;; Expand epilogue as RTL +(define_expand "epilogue" + [(return)] + "" + " +{ + m32r_expand_epilogue (); + emit_jump_insn (gen_return_normal ()); + DONE; +}") + +;; Move instructions. +;; +;; For QI and HI moves, the register must contain the full properly +;; sign-extended value. nonzero_bits assumes this [otherwise +;; SHORT_IMMEDIATES_SIGN_EXTEND must be used, but the comment for it +;; says it's a kludge and the .md files should be fixed instead]. + +(define_expand "movqi" + [(set (match_operand:QI 0 "general_operand" "") + (match_operand:QI 1 "general_operand" ""))] + "" + " +{ + /* Fixup PIC cases. */ + if (flag_pic) + { + if (symbolic_operand (operands[1], QImode)) + { + if (reload_in_progress || reload_completed) + operands[1] = m32r_legitimize_pic_address (operands[1], operands[0]); + else + operands[1] = m32r_legitimize_pic_address (operands[1], NULL_RTX); + } + } + + /* Everything except mem = const or mem = mem can be done easily. + Objects in the small data area are handled too. */ + + if (MEM_P (operands[0])) + operands[1] = force_reg (QImode, operands[1]); +}") + +(define_insn "*movqi_insn" + [(set (match_operand:QI 0 "move_dest_operand" "=r,r,r,r,r,T,m") + (match_operand:QI 1 "move_src_operand" "r,I,JQR,T,m,r,r"))] + "register_operand (operands[0], QImode) || register_operand (operands[1], QImode)" + "@ + mv %0,%1 + ldi %0,%#%1 + ldi %0,%#%1 + ldub %0,%1 + ldub %0,%1 + stb %1,%0 + stb %1,%0" + [(set_attr "type" "int2,int2,int4,load2,load4,store2,store4") + (set_attr "length" "2,2,4,2,4,2,4")]) + +(define_expand "movhi" + [(set (match_operand:HI 0 "general_operand" "") + (match_operand:HI 1 "general_operand" ""))] + "" + " +{ + /* Fixup PIC cases. */ + if (flag_pic) + { + if (symbolic_operand (operands[1], HImode)) + { + if (reload_in_progress || reload_completed) + operands[1] = m32r_legitimize_pic_address (operands[1], operands[0]); + else + operands[1] = m32r_legitimize_pic_address (operands[1], NULL_RTX); + } + } + + /* Everything except mem = const or mem = mem can be done easily. */ + + if (MEM_P (operands[0])) + operands[1] = force_reg (HImode, operands[1]); +}") + +(define_insn "*movhi_insn" + [(set (match_operand:HI 0 "move_dest_operand" "=r,r,r,r,r,r,T,m") + (match_operand:HI 1 "move_src_operand" "r,I,JQR,K,T,m,r,r"))] + "register_operand (operands[0], HImode) || register_operand (operands[1], HImode)" + "@ + mv %0,%1 + ldi %0,%#%1 + ldi %0,%#%1 + ld24 %0,%#%1 + lduh %0,%1 + lduh %0,%1 + sth %1,%0 + sth %1,%0" + [(set_attr "type" "int2,int2,int4,int4,load2,load4,store2,store4") + (set_attr "length" "2,2,4,4,2,4,2,4")]) + +(define_expand "movsi_push" + [(set (mem:SI (pre_dec:SI (match_operand:SI 0 "register_operand" ""))) + (match_operand:SI 1 "register_operand" ""))] + "" + "") + +(define_expand "movsi_pop" + [(set (match_operand:SI 0 "register_operand" "") + (mem:SI (post_inc:SI (match_operand:SI 1 "register_operand" ""))))] + "" + "") + +(define_expand "movsi" + [(set (match_operand:SI 0 "general_operand" "") + (match_operand:SI 1 "general_operand" ""))] + "" + " +{ + /* Fixup PIC cases. */ + if (flag_pic) + { + if (symbolic_operand (operands[1], SImode)) + { + if (reload_in_progress || reload_completed) + operands[1] = m32r_legitimize_pic_address (operands[1], operands[0]); + else + operands[1] = m32r_legitimize_pic_address (operands[1], NULL_RTX); + } + } + + /* Everything except mem = const or mem = mem can be done easily. */ + + if (MEM_P (operands[0])) + operands[1] = force_reg (SImode, operands[1]); + + /* Small Data Area reference? */ + if (small_data_operand (operands[1], SImode)) + { + emit_insn (gen_movsi_sda (operands[0], operands[1])); + DONE; + } + + /* If medium or large code model, symbols have to be loaded with + seth/add3. */ + if (addr32_operand (operands[1], SImode)) + { + emit_insn (gen_movsi_addr32 (operands[0], operands[1])); + DONE; + } +}") + +;; ??? Do we need a const_double constraint here for large unsigned values? +(define_insn "*movsi_insn" + [(set (match_operand:SI 0 "move_dest_operand" "=r,r,r,r,r,r,r,r,r,T,S,m") + (match_operand:SI 1 "move_src_operand" "r,I,J,MQ,L,n,T,U,m,r,r,r"))] + "register_operand (operands[0], SImode) || register_operand (operands[1], SImode)" + "* +{ + if (REG_P (operands[0]) || GET_CODE (operands[1]) == SUBREG) + { + switch (GET_CODE (operands[1])) + { + default: + break; + + case REG: + case SUBREG: + return \"mv %0,%1\"; + + case MEM: + if (GET_CODE (XEXP (operands[1], 0)) == POST_INC + && XEXP (XEXP (operands[1], 0), 0) == stack_pointer_rtx) + return \"pop %0\"; + + return \"ld %0,%1\"; + + case CONST_INT: + if (satisfies_constraint_J (operands[1])) + return \"ldi %0,%#%1\\t; %X1\"; + + if (satisfies_constraint_M (operands[1])) + return \"ld24 %0,%#%1\\t; %X1\"; + + if (satisfies_constraint_L (operands[1])) + return \"seth %0,%#%T1\\t; %X1\"; + + return \"#\"; + + case CONST: + case SYMBOL_REF: + case LABEL_REF: + if (TARGET_ADDR24) + return \"ld24 %0,%#%1\"; + + return \"#\"; + } + } + + else if (MEM_P (operands[0]) + && (REG_P (operands[1]) || GET_CODE (operands[1]) == SUBREG)) + { + if (GET_CODE (XEXP (operands[0], 0)) == PRE_DEC + && XEXP (XEXP (operands[0], 0), 0) == stack_pointer_rtx) + return \"push %1\"; + + return \"st %1,%0\"; + } + + gcc_unreachable (); +}" + [(set_attr "type" "int2,int2,int4,int4,int4,multi,load2,load2,load4,store2,store2,store4") + (set_attr "length" "2,2,4,4,4,8,2,2,4,2,2,4")]) + +; Try to use a four byte / two byte pair for constants not loadable with +; ldi, ld24, seth. + +(define_split + [(set (match_operand:SI 0 "register_operand" "") + (match_operand:SI 1 "two_insn_const_operand" ""))] + "" + [(set (match_dup 0) (match_dup 2)) + (set (match_dup 0) (ior:SI (match_dup 0) (match_dup 3)))] + " +{ + unsigned HOST_WIDE_INT val = INTVAL (operands[1]); + unsigned HOST_WIDE_INT tmp; + int shift; + + /* In all cases we will emit two instructions. However we try to + use 2 byte instructions wherever possible. We can assume the + constant isn't loadable with any of ldi, ld24, or seth. */ + + /* See if we can load a 24-bit unsigned value and invert it. */ + if (UINT24_P (~ val)) + { + emit_insn (gen_movsi (operands[0], GEN_INT (~ val))); + emit_insn (gen_one_cmplsi2 (operands[0], operands[0])); + DONE; + } + + /* See if we can load a 24-bit unsigned value and shift it into place. + 0x01fffffe is just beyond ld24's range. */ + for (shift = 1, tmp = 0x01fffffe; + shift < 8; + ++shift, tmp <<= 1) + { + if ((val & ~tmp) == 0) + { + emit_insn (gen_movsi (operands[0], GEN_INT (val >> shift))); + emit_insn (gen_ashlsi3 (operands[0], operands[0], GEN_INT (shift))); + DONE; + } + } + + /* Can't use any two byte insn, fall back to seth/or3. Use ~0xffff instead + of 0xffff0000, since the later fails on a 64-bit host. */ + operands[2] = GEN_INT ((val) & ~0xffff); + operands[3] = GEN_INT ((val) & 0xffff); +}") + +(define_split + [(set (match_operand:SI 0 "register_operand" "") + (match_operand:SI 1 "seth_add3_operand" ""))] + "TARGET_ADDR32" + [(set (match_dup 0) + (high:SI (match_dup 1))) + (set (match_dup 0) + (lo_sum:SI (match_dup 0) + (match_dup 1)))] + "") + +;; Small data area support. +;; The address of _SDA_BASE_ is loaded into a register and all objects in +;; the small data area are indexed off that. This is done for each reference +;; but cse will clean things up for us. We let the compiler choose the +;; register to use so we needn't allocate (and maybe even fix) a special +;; register to use. Since the load and store insns have a 16-bit offset the +;; total size of the data area can be 64K. However, if the data area lives +;; above 16M (24 bits), _SDA_BASE_ will have to be loaded with seth/add3 which +;; would then yield 3 instructions to reference an object [though there would +;; be no net loss if two or more objects were referenced]. The 3 insns can be +;; reduced back to 2 if the size of the small data area were reduced to 32K +;; [then seth + ld/st would work for any object in the area]. Doing this +;; would require special handling of _SDA_BASE_ (its value would be +;; (.sdata + 32K) & 0xffff0000) and reloc computations would be different +;; [I think]. What to do about this is deferred until later and for now we +;; require .sdata to be in the first 16M. + +(define_expand "movsi_sda" + [(set (match_dup 2) + (unspec:SI [(const_int 0)] UNSPEC_LOAD_SDA_BASE)) + (set (match_operand:SI 0 "register_operand" "") + (lo_sum:SI (match_dup 2) + (match_operand:SI 1 "small_data_operand" "")))] + "" + " +{ + if (reload_in_progress || reload_completed) + operands[2] = operands[0]; + else + operands[2] = gen_reg_rtx (SImode); +}") + +(define_insn "*load_sda_base_32" + [(set (match_operand:SI 0 "register_operand" "=r") + (unspec:SI [(const_int 0)] UNSPEC_LOAD_SDA_BASE))] + "TARGET_ADDR32" + "seth %0,%#shigh(_SDA_BASE_)\;add3 %0,%0,%#low(_SDA_BASE_)" + [(set_attr "type" "multi") + (set_attr "length" "8")]) + +(define_insn "*load_sda_base" + [(set (match_operand:SI 0 "register_operand" "=r") + (unspec:SI [(const_int 0)] UNSPEC_LOAD_SDA_BASE))] + "" + "ld24 %0,#_SDA_BASE_" + [(set_attr "type" "int4") + (set_attr "length" "4")]) + +;; 32-bit address support. + +(define_expand "movsi_addr32" + [(set (match_dup 2) + ; addr32_operand isn't used because it's too restrictive, + ; seth_add3_operand is more general and thus safer. + (high:SI (match_operand:SI 1 "seth_add3_operand" ""))) + (set (match_operand:SI 0 "register_operand" "") + (lo_sum:SI (match_dup 2) (match_dup 1)))] + "" + " +{ + if (reload_in_progress || reload_completed) + operands[2] = operands[0]; + else + operands[2] = gen_reg_rtx (SImode); +}") + +(define_insn "set_hi_si" + [(set (match_operand:SI 0 "register_operand" "=r") + (high:SI (match_operand 1 "symbolic_operand" "")))] + "" + "seth %0,%#shigh(%1)" + [(set_attr "type" "int4") + (set_attr "length" "4")]) + +(define_insn "lo_sum_si" + [(set (match_operand:SI 0 "register_operand" "=r") + (lo_sum:SI (match_operand:SI 1 "register_operand" "r") + (match_operand:SI 2 "immediate_operand" "in")))] + "" + "add3 %0,%1,%#%B2" + [(set_attr "type" "int4") + (set_attr "length" "4")]) + +(define_expand "movdi" + [(set (match_operand:DI 0 "general_operand" "") + (match_operand:DI 1 "general_operand" ""))] + "" + " +{ + /* Fixup PIC cases. */ + if (flag_pic) + { + if (symbolic_operand (operands[1], DImode)) + { + if (reload_in_progress || reload_completed) + operands[1] = m32r_legitimize_pic_address (operands[1], operands[0]); + else + operands[1] = m32r_legitimize_pic_address (operands[1], NULL_RTX); + } + } + + /* Everything except mem = const or mem = mem can be done easily. */ + + if (MEM_P (operands[0])) + operands[1] = force_reg (DImode, operands[1]); +}") + +(define_insn "*movdi_insn" + [(set (match_operand:DI 0 "move_dest_operand" "=r,r,r,r,m") + (match_operand:DI 1 "move_double_src_operand" "r,nG,F,m,r"))] + "register_operand (operands[0], DImode) || register_operand (operands[1], DImode)" + "#" + [(set_attr "type" "multi,multi,multi,load8,store8") + (set_attr "length" "4,4,16,6,6")]) + +(define_split + [(set (match_operand:DI 0 "move_dest_operand" "") + (match_operand:DI 1 "move_double_src_operand" ""))] + "reload_completed" + [(match_dup 2)] + "operands[2] = gen_split_move_double (operands);") + +;; Floating point move insns. + +(define_expand "movsf" + [(set (match_operand:SF 0 "general_operand" "") + (match_operand:SF 1 "general_operand" ""))] + "" + " +{ + /* Fixup PIC cases. */ + if (flag_pic) + { + if (symbolic_operand (operands[1], SFmode)) + { + if (reload_in_progress || reload_completed) + operands[1] = m32r_legitimize_pic_address (operands[1], operands[0]); + else + operands[1] = m32r_legitimize_pic_address (operands[1], NULL_RTX); + } + } + + /* Everything except mem = const or mem = mem can be done easily. */ + + if (MEM_P (operands[0])) + operands[1] = force_reg (SFmode, operands[1]); +}") + +(define_insn "*movsf_insn" + [(set (match_operand:SF 0 "move_dest_operand" "=r,r,r,r,r,T,S,m") + (match_operand:SF 1 "move_src_operand" "r,F,U,S,m,r,r,r"))] + "register_operand (operands[0], SFmode) || register_operand (operands[1], SFmode)" + "@ + mv %0,%1 + # + ld %0,%1 + ld %0,%1 + ld %0,%1 + st %1,%0 + st %1,%0 + st %1,%0" + ;; ??? Length of alternative 1 is either 2, 4 or 8. + [(set_attr "type" "int2,multi,load2,load2,load4,store2,store2,store4") + (set_attr "length" "2,8,2,2,4,2,2,4")]) + +(define_split + [(set (match_operand:SF 0 "register_operand" "") + (match_operand:SF 1 "const_double_operand" ""))] + "reload_completed" + [(set (match_dup 2) (match_dup 3))] + " +{ + operands[2] = operand_subword (operands[0], 0, 0, SFmode); + operands[3] = operand_subword (operands[1], 0, 0, SFmode); +}") + +(define_expand "movdf" + [(set (match_operand:DF 0 "general_operand" "") + (match_operand:DF 1 "general_operand" ""))] + "" + " +{ + /* Fixup PIC cases. */ + if (flag_pic) + { + if (symbolic_operand (operands[1], DFmode)) + { + if (reload_in_progress || reload_completed) + operands[1] = m32r_legitimize_pic_address (operands[1], operands[0]); + else + operands[1] = m32r_legitimize_pic_address (operands[1], NULL_RTX); + } + } + + /* Everything except mem = const or mem = mem can be done easily. */ + + if (MEM_P (operands[0])) + operands[1] = force_reg (DFmode, operands[1]); +}") + +(define_insn "*movdf_insn" + [(set (match_operand:DF 0 "move_dest_operand" "=r,r,r,m") + (match_operand:DF 1 "move_double_src_operand" "r,F,m,r"))] + "register_operand (operands[0], DFmode) || register_operand (operands[1], DFmode)" + "#" + [(set_attr "type" "multi,multi,load8,store8") + (set_attr "length" "4,16,6,6")]) + +(define_split + [(set (match_operand:DF 0 "move_dest_operand" "") + (match_operand:DF 1 "move_double_src_operand" ""))] + "reload_completed" + [(match_dup 2)] + "operands[2] = gen_split_move_double (operands);") + +;; Zero extension instructions. + +(define_insn "zero_extendqihi2" + [(set (match_operand:HI 0 "register_operand" "=r,r,r") + (zero_extend:HI (match_operand:QI 1 "extend_operand" "r,T,m")))] + "" + "@ + and3 %0,%1,%#255 + ldub %0,%1 + ldub %0,%1" + [(set_attr "type" "int4,load2,load4") + (set_attr "length" "4,2,4")]) + +(define_insn "zero_extendqisi2" + [(set (match_operand:SI 0 "register_operand" "=r,r,r") + (zero_extend:SI (match_operand:QI 1 "extend_operand" "r,T,m")))] + "" + "@ + and3 %0,%1,%#255 + ldub %0,%1 + ldub %0,%1" + [(set_attr "type" "int4,load2,load4") + (set_attr "length" "4,2,4")]) + +(define_insn "zero_extendhisi2" + [(set (match_operand:SI 0 "register_operand" "=r,r,r") + (zero_extend:SI (match_operand:HI 1 "extend_operand" "r,T,m")))] + "" + "@ + and3 %0,%1,%#65535 + lduh %0,%1 + lduh %0,%1" + [(set_attr "type" "int4,load2,load4") + (set_attr "length" "4,2,4")]) + +;; Signed conversions from a smaller integer to a larger integer +(define_insn "extendqihi2" + [(set (match_operand:HI 0 "register_operand" "=r,r,r") + (sign_extend:HI (match_operand:QI 1 "extend_operand" "0,T,m")))] + "" + "@ + # + ldb %0,%1 + ldb %0,%1" + [(set_attr "type" "multi,load2,load4") + (set_attr "length" "2,2,4")]) + +(define_split + [(set (match_operand:HI 0 "register_operand" "") + (sign_extend:HI (match_operand:QI 1 "register_operand" "")))] + "reload_completed" + [(match_dup 2) + (match_dup 3)] + " +{ + rtx op0 = gen_lowpart (SImode, operands[0]); + rtx shift = GEN_INT (24); + + operands[2] = gen_ashlsi3 (op0, op0, shift); + operands[3] = gen_ashrsi3 (op0, op0, shift); +}") + +(define_insn "extendqisi2" + [(set (match_operand:SI 0 "register_operand" "=r,r,r") + (sign_extend:SI (match_operand:QI 1 "extend_operand" "0,T,m")))] + "" + "@ + # + ldb %0,%1 + ldb %0,%1" + [(set_attr "type" "multi,load2,load4") + (set_attr "length" "4,2,4")]) + +(define_split + [(set (match_operand:SI 0 "register_operand" "") + (sign_extend:SI (match_operand:QI 1 "register_operand" "")))] + "reload_completed" + [(match_dup 2) + (match_dup 3)] + " +{ + rtx shift = GEN_INT (24); + + operands[2] = gen_ashlsi3 (operands[0], operands[0], shift); + operands[3] = gen_ashrsi3 (operands[0], operands[0], shift); +}") + +(define_insn "extendhisi2" + [(set (match_operand:SI 0 "register_operand" "=r,r,r") + (sign_extend:SI (match_operand:HI 1 "extend_operand" "0,T,m")))] + "" + "@ + # + ldh %0,%1 + ldh %0,%1" + [(set_attr "type" "multi,load2,load4") + (set_attr "length" "4,2,4")]) + +(define_split + [(set (match_operand:SI 0 "register_operand" "") + (sign_extend:SI (match_operand:HI 1 "register_operand" "")))] + "reload_completed" + [(match_dup 2) + (match_dup 3)] + " +{ + rtx shift = GEN_INT (16); + + operands[2] = gen_ashlsi3 (operands[0], operands[0], shift); + operands[3] = gen_ashrsi3 (operands[0], operands[0], shift); +}") + +;; Arithmetic instructions. + +; ??? Adding an alternative to split add3 of small constants into two +; insns yields better instruction packing but slower code. Adds of small +; values is done a lot. + +(define_insn "addsi3" + [(set (match_operand:SI 0 "register_operand" "=r,r,r") + (plus:SI (match_operand:SI 1 "register_operand" "%0,0,r") + (match_operand:SI 2 "nonmemory_operand" "r,I,J")))] + "" + "@ + add %0,%2 + addi %0,%#%2 + add3 %0,%1,%#%2" + [(set_attr "type" "int2,int2,int4") + (set_attr "length" "2,2,4")]) + +;(define_split +; [(set (match_operand:SI 0 "register_operand" "") +; (plus:SI (match_operand:SI 1 "register_operand" "") +; (match_operand:SI 2 "int8_operand" "")))] +; "reload_completed +; && REGNO (operands[0]) != REGNO (operands[1]) +; && satisfies_constraint_I (operands[2]) +; && INTVAL (operands[2]) != 0" +; [(set (match_dup 0) (match_dup 1)) +; (set (match_dup 0) (plus:SI (match_dup 0) (match_dup 2)))] +; "") + +(define_insn "adddi3" + [(set (match_operand:DI 0 "register_operand" "=r") + (plus:DI (match_operand:DI 1 "register_operand" "%0") + (match_operand:DI 2 "register_operand" "r"))) + (clobber (reg:CC 17))] + "" + "#" + [(set_attr "type" "multi") + (set_attr "length" "6")]) + +;; ??? The cmp clears the condition bit. Can we speed up somehow? +(define_split + [(set (match_operand:DI 0 "register_operand" "") + (plus:DI (match_operand:DI 1 "register_operand" "") + (match_operand:DI 2 "register_operand" ""))) + (clobber (reg:CC 17))] + "reload_completed" + [(parallel [(set (reg:CC 17) + (const_int 0)) + (use (match_dup 4))]) + (parallel [(set (match_dup 4) + (plus:SI (match_dup 4) + (plus:SI (match_dup 5) + (ne:SI (reg:CC 17) (const_int 0))))) + (set (reg:CC 17) + (unspec:CC [(const_int 0)] UNSPEC_SET_CBIT))]) + (parallel [(set (match_dup 6) + (plus:SI (match_dup 6) + (plus:SI (match_dup 7) + (ne:SI (reg:CC 17) (const_int 0))))) + (set (reg:CC 17) + (unspec:CC [(const_int 0)] UNSPEC_SET_CBIT))])] + " +{ + operands[4] = operand_subword (operands[0], (WORDS_BIG_ENDIAN != 0), 0, DImode); + operands[5] = operand_subword (operands[2], (WORDS_BIG_ENDIAN != 0), 0, DImode); + operands[6] = operand_subword (operands[0], (WORDS_BIG_ENDIAN == 0), 0, DImode); + operands[7] = operand_subword (operands[2], (WORDS_BIG_ENDIAN == 0), 0, DImode); +}") + +(define_insn "*clear_c" + [(set (reg:CC 17) + (const_int 0)) + (use (match_operand:SI 0 "register_operand" "r"))] + "" + "cmp %0,%0" + [(set_attr "type" "int2") + (set_attr "length" "2")]) + +(define_insn "*add_carry" + [(set (match_operand:SI 0 "register_operand" "=r") + (plus:SI (match_operand:SI 1 "register_operand" "%0") + (plus:SI (match_operand:SI 2 "register_operand" "r") + (ne:SI (reg:CC 17) (const_int 0))))) + (set (reg:CC 17) + (unspec:CC [(const_int 0)] UNSPEC_SET_CBIT))] + "" + "addx %0,%2" + [(set_attr "type" "int2") + (set_attr "length" "2")]) + +(define_insn "subsi3" + [(set (match_operand:SI 0 "register_operand" "=r") + (minus:SI (match_operand:SI 1 "register_operand" "0") + (match_operand:SI 2 "register_operand" "r")))] + "" + "sub %0,%2" + [(set_attr "type" "int2") + (set_attr "length" "2")]) + +(define_insn "subdi3" + [(set (match_operand:DI 0 "register_operand" "=r") + (minus:DI (match_operand:DI 1 "register_operand" "0") + (match_operand:DI 2 "register_operand" "r"))) + (clobber (reg:CC 17))] + "" + "#" + [(set_attr "type" "multi") + (set_attr "length" "6")]) + +;; ??? The cmp clears the condition bit. Can we speed up somehow? +(define_split + [(set (match_operand:DI 0 "register_operand" "") + (minus:DI (match_operand:DI 1 "register_operand" "") + (match_operand:DI 2 "register_operand" ""))) + (clobber (reg:CC 17))] + "reload_completed" + [(parallel [(set (reg:CC 17) + (const_int 0)) + (use (match_dup 4))]) + (parallel [(set (match_dup 4) + (minus:SI (match_dup 4) + (minus:SI (match_dup 5) + (ne:SI (reg:CC 17) (const_int 0))))) + (set (reg:CC 17) + (unspec:CC [(const_int 0)] UNSPEC_SET_CBIT))]) + (parallel [(set (match_dup 6) + (minus:SI (match_dup 6) + (minus:SI (match_dup 7) + (ne:SI (reg:CC 17) (const_int 0))))) + (set (reg:CC 17) + (unspec:CC [(const_int 0)] UNSPEC_SET_CBIT))])] + " +{ + operands[4] = operand_subword (operands[0], (WORDS_BIG_ENDIAN != 0), 0, DImode); + operands[5] = operand_subword (operands[2], (WORDS_BIG_ENDIAN != 0), 0, DImode); + operands[6] = operand_subword (operands[0], (WORDS_BIG_ENDIAN == 0), 0, DImode); + operands[7] = operand_subword (operands[2], (WORDS_BIG_ENDIAN == 0), 0, DImode); +}") + +(define_insn "*sub_carry" + [(set (match_operand:SI 0 "register_operand" "=r") + (minus:SI (match_operand:SI 1 "register_operand" "%0") + (minus:SI (match_operand:SI 2 "register_operand" "r") + (ne:SI (reg:CC 17) (const_int 0))))) + (set (reg:CC 17) + (unspec:CC [(const_int 0)] UNSPEC_SET_CBIT))] + "" + "subx %0,%2" + [(set_attr "type" "int2") + (set_attr "length" "2")]) + +; Multiply/Divide instructions. + +(define_insn "mulhisi3" + [(set (match_operand:SI 0 "register_operand" "=r") + (mult:SI (sign_extend:SI (match_operand:HI 1 "register_operand" "r")) + (sign_extend:SI (match_operand:HI 2 "register_operand" "r"))))] + "" + "mullo %1,%2\;mvfacmi %0" + [(set_attr "type" "multi") + (set_attr "length" "4")]) + +(define_insn "mulsi3" + [(set (match_operand:SI 0 "register_operand" "=r") + (mult:SI (match_operand:SI 1 "register_operand" "%0") + (match_operand:SI 2 "register_operand" "r")))] + "" + "mul %0,%2" + [(set_attr "type" "mul2") + (set_attr "length" "2")]) + +(define_insn "divsi3" + [(set (match_operand:SI 0 "register_operand" "=r") + (div:SI (match_operand:SI 1 "register_operand" "0") + (match_operand:SI 2 "register_operand" "r")))] + "" + "div %0,%2" + [(set_attr "type" "div4") + (set_attr "length" "4")]) + +(define_insn "udivsi3" + [(set (match_operand:SI 0 "register_operand" "=r") + (udiv:SI (match_operand:SI 1 "register_operand" "0") + (match_operand:SI 2 "register_operand" "r")))] + "" + "divu %0,%2" + [(set_attr "type" "div4") + (set_attr "length" "4")]) + +(define_insn "modsi3" + [(set (match_operand:SI 0 "register_operand" "=r") + (mod:SI (match_operand:SI 1 "register_operand" "0") + (match_operand:SI 2 "register_operand" "r")))] + "" + "rem %0,%2" + [(set_attr "type" "div4") + (set_attr "length" "4")]) + +(define_insn "umodsi3" + [(set (match_operand:SI 0 "register_operand" "=r") + (umod:SI (match_operand:SI 1 "register_operand" "0") + (match_operand:SI 2 "register_operand" "r")))] + "" + "remu %0,%2" + [(set_attr "type" "div4") + (set_attr "length" "4")]) + +;; Boolean instructions. +;; +;; We don't define the DImode versions as expand_binop does a good enough job. +;; And if it doesn't it should be fixed. + +(define_insn "andsi3" + [(set (match_operand:SI 0 "register_operand" "=r,r") + (and:SI (match_operand:SI 1 "register_operand" "%0,r") + (match_operand:SI 2 "reg_or_uint16_operand" "r,K")))] + "" + "* +{ + /* If we are worried about space, see if we can break this up into two + short instructions, which might eliminate a NOP being inserted. */ + if (optimize_size + && m32r_not_same_reg (operands[0], operands[1]) + && satisfies_constraint_I (operands[2])) + return \"#\"; + + else if (CONST_INT_P (operands[2])) + return \"and3 %0,%1,%#%X2\"; + + return \"and %0,%2\"; +}" + [(set_attr "type" "int2,int4") + (set_attr "length" "2,4")]) + +(define_split + [(set (match_operand:SI 0 "register_operand" "") + (and:SI (match_operand:SI 1 "register_operand" "") + (match_operand:SI 2 "int8_operand" "")))] + "optimize_size && m32r_not_same_reg (operands[0], operands[1])" + [(set (match_dup 0) (match_dup 2)) + (set (match_dup 0) (and:SI (match_dup 0) (match_dup 1)))] + "") + +(define_insn "iorsi3" + [(set (match_operand:SI 0 "register_operand" "=r,r") + (ior:SI (match_operand:SI 1 "register_operand" "%0,r") + (match_operand:SI 2 "reg_or_uint16_operand" "r,K")))] + "" + "* +{ + /* If we are worried about space, see if we can break this up into two + short instructions, which might eliminate a NOP being inserted. */ + if (optimize_size + && m32r_not_same_reg (operands[0], operands[1]) + && satisfies_constraint_I (operands[2])) + return \"#\"; + + else if (CONST_INT_P (operands[2])) + return \"or3 %0,%1,%#%X2\"; + + return \"or %0,%2\"; +}" + [(set_attr "type" "int2,int4") + (set_attr "length" "2,4")]) + +(define_split + [(set (match_operand:SI 0 "register_operand" "") + (ior:SI (match_operand:SI 1 "register_operand" "") + (match_operand:SI 2 "int8_operand" "")))] + "optimize_size && m32r_not_same_reg (operands[0], operands[1])" + [(set (match_dup 0) (match_dup 2)) + (set (match_dup 0) (ior:SI (match_dup 0) (match_dup 1)))] + "") + +(define_insn "xorsi3" + [(set (match_operand:SI 0 "register_operand" "=r,r") + (xor:SI (match_operand:SI 1 "register_operand" "%0,r") + (match_operand:SI 2 "reg_or_uint16_operand" "r,K")))] + "" + "* +{ + /* If we are worried about space, see if we can break this up into two + short instructions, which might eliminate a NOP being inserted. */ + if (optimize_size + && m32r_not_same_reg (operands[0], operands[1]) + && satisfies_constraint_I (operands[2])) + return \"#\"; + + else if (CONST_INT_P (operands[2])) + return \"xor3 %0,%1,%#%X2\"; + + return \"xor %0,%2\"; +}" + [(set_attr "type" "int2,int4") + (set_attr "length" "2,4")]) + +(define_split + [(set (match_operand:SI 0 "register_operand" "") + (xor:SI (match_operand:SI 1 "register_operand" "") + (match_operand:SI 2 "int8_operand" "")))] + "optimize_size && m32r_not_same_reg (operands[0], operands[1])" + [(set (match_dup 0) (match_dup 2)) + (set (match_dup 0) (xor:SI (match_dup 0) (match_dup 1)))] + "") + +(define_insn "negsi2" + [(set (match_operand:SI 0 "register_operand" "=r") + (neg:SI (match_operand:SI 1 "register_operand" "r")))] + "" + "neg %0,%1" + [(set_attr "type" "int2") + (set_attr "length" "2")]) + +(define_insn "one_cmplsi2" + [(set (match_operand:SI 0 "register_operand" "=r") + (not:SI (match_operand:SI 1 "register_operand" "r")))] + "" + "not %0,%1" + [(set_attr "type" "int2") + (set_attr "length" "2")]) + +;; Shift instructions. + +(define_insn "ashlsi3" + [(set (match_operand:SI 0 "register_operand" "=r,r,r") + (ashift:SI (match_operand:SI 1 "register_operand" "0,0,r") + (match_operand:SI 2 "reg_or_uint16_operand" "r,O,K")))] + "" + "@ + sll %0,%2 + slli %0,%#%2 + sll3 %0,%1,%#%2" + [(set_attr "type" "shift2,shift2,shift4") + (set_attr "length" "2,2,4")]) + +(define_insn "ashrsi3" + [(set (match_operand:SI 0 "register_operand" "=r,r,r") + (ashiftrt:SI (match_operand:SI 1 "register_operand" "0,0,r") + (match_operand:SI 2 "reg_or_uint16_operand" "r,O,K")))] + "" + "@ + sra %0,%2 + srai %0,%#%2 + sra3 %0,%1,%#%2" + [(set_attr "type" "shift2,shift2,shift4") + (set_attr "length" "2,2,4")]) + +(define_insn "lshrsi3" + [(set (match_operand:SI 0 "register_operand" "=r,r,r") + (lshiftrt:SI (match_operand:SI 1 "register_operand" "0,0,r") + (match_operand:SI 2 "reg_or_uint16_operand" "r,O,K")))] + "" + "@ + srl %0,%2 + srli %0,%#%2 + srl3 %0,%1,%#%2" + [(set_attr "type" "shift2,shift2,shift4") + (set_attr "length" "2,2,4")]) + +;; Compare instructions. +;; This controls RTL generation and register allocation. + +;; We generate RTL for comparisons and branches by having the cmpxx +;; patterns store away the operands. Then the bcc patterns +;; emit RTL for both the compare and the branch. +;; +;; On the m32r it is more efficient to use the bxxz instructions and +;; thus merge the compare and branch into one instruction, so they are +;; preferred. + +(define_insn "cmp_eqsi_zero_insn" + [(set (reg:CC 17) + (eq:CC (match_operand:SI 0 "register_operand" "r,r") + (match_operand:SI 1 "reg_or_zero_operand" "r,P")))] + "TARGET_M32RX || TARGET_M32R2" + "@ + cmpeq %0, %1 + cmpz %0" + [(set_attr "type" "int4") + (set_attr "length" "4")]) + +;; The cmp_xxx_insn patterns set the condition bit to the result of the +;; comparison. There isn't a "compare equal" instruction so cmp_eqsi_insn +;; is quite inefficient. However, it is rarely used. + +(define_insn "cmp_eqsi_insn" + [(set (reg:CC 17) + (eq:CC (match_operand:SI 0 "register_operand" "r,r") + (match_operand:SI 1 "reg_or_cmp_int16_operand" "r,P"))) + (clobber (match_scratch:SI 2 "=&r,&r"))] + "" + "* +{ + if (which_alternative == 0) + { + return \"mv %2,%0\;sub %2,%1\;cmpui %2,#1\"; + } + else + { + if (INTVAL (operands [1]) == 0) + return \"cmpui %0, #1\"; + else if (REGNO (operands [2]) == REGNO (operands [0])) + return \"addi %0,%#%N1\;cmpui %2,#1\"; + else + return \"add3 %2,%0,%#%N1\;cmpui %2,#1\"; + } +}" + [(set_attr "type" "multi,multi") + (set_attr "length" "8,8")]) + +(define_insn "cmp_ltsi_insn" + [(set (reg:CC 17) + (lt:CC (match_operand:SI 0 "register_operand" "r,r") + (match_operand:SI 1 "reg_or_int16_operand" "r,J")))] + "" + "@ + cmp %0,%1 + cmpi %0,%#%1" + [(set_attr "type" "int2,int4") + (set_attr "length" "2,4")]) + +(define_insn "cmp_ltusi_insn" + [(set (reg:CC 17) + (ltu:CC (match_operand:SI 0 "register_operand" "r,r") + (match_operand:SI 1 "reg_or_int16_operand" "r,J")))] + "" + "@ + cmpu %0,%1 + cmpui %0,%#%1" + [(set_attr "type" "int2,int4") + (set_attr "length" "2,4")]) + +;; These control RTL generation for conditional jump insns. + +(define_expand "cbranchsi4" + ; the comparison is emitted by gen_compare if needed. + [(set (pc) + (if_then_else (match_operator 0 "ordered_comparison_operator" + [(match_operand:SI 1 "register_operand" "") + (match_operand:SI 2 "reg_or_cmp_int16_operand" "")]) + (label_ref (match_operand 3 "" "")) + (pc)))] + "" + " +{ + operands[0] = gen_compare (GET_CODE (operands[0]), operands[1], operands[2], FALSE); + operands[1] = XEXP (operands[0], 0); + operands[2] = XEXP (operands[0], 1); +}") + +;; Now match both normal and inverted jump. + +(define_insn "*branch_insn" + [(set (pc) + (if_then_else (match_operator 1 "eqne_comparison_operator" + [(reg 17) (const_int 0)]) + (label_ref (match_operand 0 "" "")) + (pc)))] + "" + "* +{ + static char instruction[40]; + sprintf (instruction, \"%s%s %%l0\", + (GET_CODE (operands[1]) == NE) ? \"bc\" : \"bnc\", + (get_attr_length (insn) == 2) ? \".s\" : \"\"); + return instruction; +}" + [(set_attr "type" "branch") + ; cf PR gcc/28508 + ; We use 300/600 instead of 512,1024 to account for inaccurate insn + ; lengths and insn alignments that are complex to track. + ; It's not important that we be hyper-precise here. It may be more + ; important blah blah blah when the chip supports parallel execution + ; blah blah blah but until then blah blah blah this is simple and + ; suffices. + (set (attr "length") (if_then_else (ltu (plus (minus (match_dup 0) (pc)) + (const_int 300)) + (const_int 600)) + (const_int 2) + (const_int 4)))]) + +(define_insn "*rev_branch_insn" + [(set (pc) + (if_then_else (match_operator 1 "eqne_comparison_operator" + [(reg 17) (const_int 0)]) + (pc) + (label_ref (match_operand 0 "" ""))))] + ;"REVERSIBLE_CC_MODE (GET_MODE (XEXP (operands[1], 0)))" + "" + "* +{ + static char instruction[40]; + sprintf (instruction, \"%s%s %%l0\", + (GET_CODE (operands[1]) == EQ) ? \"bc\" : \"bnc\", + (get_attr_length (insn) == 2) ? \".s\" : \"\"); + return instruction; +}" + [(set_attr "type" "branch") + ; cf PR gcc/28508 + ; We use 300/600 instead of 512,1024 to account for inaccurate insn + ; lengths and insn alignments that are complex to track. + ; It's not important that we be hyper-precise here. It may be more + ; important blah blah blah when the chip supports parallel execution + ; blah blah blah but until then blah blah blah this is simple and + ; suffices. + (set (attr "length") (if_then_else (ltu (plus (minus (match_dup 0) (pc)) + (const_int 300)) + (const_int 600)) + (const_int 2) + (const_int 4)))]) + +; reg/reg compare and branch insns + +(define_insn "*reg_branch_insn" + [(set (pc) + (if_then_else (match_operator 1 "eqne_comparison_operator" + [(match_operand:SI 2 "register_operand" "r") + (match_operand:SI 3 "register_operand" "r")]) + (label_ref (match_operand 0 "" "")) + (pc)))] + "" + "* +{ + /* Is branch target reachable with beq/bne? */ + if (get_attr_length (insn) == 4) + { + if (GET_CODE (operands[1]) == EQ) + return \"beq %2,%3,%l0\"; + else + return \"bne %2,%3,%l0\"; + } + else + { + if (GET_CODE (operands[1]) == EQ) + return \"bne %2,%3,1f\;bra %l0\;1:\"; + else + return \"beq %2,%3,1f\;bra %l0\;1:\"; + } +}" + [(set_attr "type" "branch") + ; We use 25000/50000 instead of 32768/65536 to account for slot filling + ; which is complex to track and inaccurate length specs. + (set (attr "length") (if_then_else (ltu (plus (minus (match_dup 0) (pc)) + (const_int 25000)) + (const_int 50000)) + (const_int 4) + (const_int 8)))]) + +(define_insn "*rev_reg_branch_insn" + [(set (pc) + (if_then_else (match_operator 1 "eqne_comparison_operator" + [(match_operand:SI 2 "register_operand" "r") + (match_operand:SI 3 "register_operand" "r")]) + (pc) + (label_ref (match_operand 0 "" ""))))] + "" + "* +{ + /* Is branch target reachable with beq/bne? */ + if (get_attr_length (insn) == 4) + { + if (GET_CODE (operands[1]) == NE) + return \"beq %2,%3,%l0\"; + else + return \"bne %2,%3,%l0\"; + } + else + { + if (GET_CODE (operands[1]) == NE) + return \"bne %2,%3,1f\;bra %l0\;1:\"; + else + return \"beq %2,%3,1f\;bra %l0\;1:\"; + } +}" + [(set_attr "type" "branch") + ; We use 25000/50000 instead of 32768/65536 to account for slot filling + ; which is complex to track and inaccurate length specs. + (set (attr "length") (if_then_else (ltu (plus (minus (match_dup 0) (pc)) + (const_int 25000)) + (const_int 50000)) + (const_int 4) + (const_int 8)))]) + +; reg/zero compare and branch insns + +(define_insn "*zero_branch_insn" + [(set (pc) + (if_then_else (match_operator 1 "signed_comparison_operator" + [(match_operand:SI 2 "register_operand" "r") + (const_int 0)]) + (label_ref (match_operand 0 "" "")) + (pc)))] + "" + "* +{ + const char *br,*invbr; + char asmtext[40]; + + switch (GET_CODE (operands[1])) + { + case EQ : br = \"eq\"; invbr = \"ne\"; break; + case NE : br = \"ne\"; invbr = \"eq\"; break; + case LE : br = \"le\"; invbr = \"gt\"; break; + case GT : br = \"gt\"; invbr = \"le\"; break; + case LT : br = \"lt\"; invbr = \"ge\"; break; + case GE : br = \"ge\"; invbr = \"lt\"; break; + + default: gcc_unreachable (); + } + + /* Is branch target reachable with bxxz? */ + if (get_attr_length (insn) == 4) + { + sprintf (asmtext, \"b%sz %%2,%%l0\", br); + output_asm_insn (asmtext, operands); + } + else + { + sprintf (asmtext, \"b%sz %%2,1f\;bra %%l0\;1:\", invbr); + output_asm_insn (asmtext, operands); + } + return \"\"; +}" + [(set_attr "type" "branch") + ; We use 25000/50000 instead of 32768/65536 to account for slot filling + ; which is complex to track and inaccurate length specs. + (set (attr "length") (if_then_else (ltu (plus (minus (match_dup 0) (pc)) + (const_int 25000)) + (const_int 50000)) + (const_int 4) + (const_int 8)))]) + +(define_insn "*rev_zero_branch_insn" + [(set (pc) + (if_then_else (match_operator 1 "eqne_comparison_operator" + [(match_operand:SI 2 "register_operand" "r") + (const_int 0)]) + (pc) + (label_ref (match_operand 0 "" ""))))] + "" + "* +{ + const char *br,*invbr; + char asmtext[40]; + + switch (GET_CODE (operands[1])) + { + case EQ : br = \"eq\"; invbr = \"ne\"; break; + case NE : br = \"ne\"; invbr = \"eq\"; break; + case LE : br = \"le\"; invbr = \"gt\"; break; + case GT : br = \"gt\"; invbr = \"le\"; break; + case LT : br = \"lt\"; invbr = \"ge\"; break; + case GE : br = \"ge\"; invbr = \"lt\"; break; + + default: gcc_unreachable (); + } + + /* Is branch target reachable with bxxz? */ + if (get_attr_length (insn) == 4) + { + sprintf (asmtext, \"b%sz %%2,%%l0\", invbr); + output_asm_insn (asmtext, operands); + } + else + { + sprintf (asmtext, \"b%sz %%2,1f\;bra %%l0\;1:\", br); + output_asm_insn (asmtext, operands); + } + return \"\"; +}" + [(set_attr "type" "branch") + ; We use 25000/50000 instead of 32768/65536 to account for slot filling + ; which is complex to track and inaccurate length specs. + (set (attr "length") (if_then_else (ltu (plus (minus (match_dup 0) (pc)) + (const_int 25000)) + (const_int 50000)) + (const_int 4) + (const_int 8)))]) + +;; S operations to set a register to 1/0 based on a comparison + +(define_expand "cstoresi4" + [(match_operand:SI 0 "register_operand" "") + (match_operator:SI 1 "ordered_comparison_operator" + [(match_operand:SI 2 "register_operand" "") + (match_operand:SI 3 "reg_or_cmp_int16_operand" "")])] + "" + " +{ + if (GET_MODE (operands[0]) != SImode) + FAIL; + + if (!gen_cond_store (GET_CODE (operands[1]), + operands[0], operands[2], operands[3])) + FAIL; + + DONE; +}") + +(define_insn "seq_insn_m32rx" + [(set (match_operand:SI 0 "register_operand" "=r") + (eq:SI (match_operand:SI 1 "register_operand" "%r") + (match_operand:SI 2 "reg_or_zero_operand" "rP"))) + (clobber (reg:CC 17))] + "TARGET_M32RX || TARGET_M32R2" + "#" + [(set_attr "type" "multi") + (set_attr "length" "6")]) + +(define_split + [(set (match_operand:SI 0 "register_operand" "") + (eq:SI (match_operand:SI 1 "register_operand" "") + (match_operand:SI 2 "reg_or_zero_operand" ""))) + (clobber (reg:CC 17))] + "TARGET_M32RX || TARGET_M32R2" + [(set (reg:CC 17) + (eq:CC (match_dup 1) + (match_dup 2))) + (set (match_dup 0) + (ne:SI (reg:CC 17) (const_int 0)))] + "") + +(define_insn "seq_zero_insn" + [(set (match_operand:SI 0 "register_operand" "=r") + (eq:SI (match_operand:SI 1 "register_operand" "r") + (const_int 0))) + (clobber (reg:CC 17))] + "TARGET_M32R" + "#" + [(set_attr "type" "multi") + (set_attr "length" "6")]) + +(define_split + [(set (match_operand:SI 0 "register_operand" "") + (eq:SI (match_operand:SI 1 "register_operand" "") + (const_int 0))) + (clobber (reg:CC 17))] + "TARGET_M32R" + [(match_dup 3)] + " +{ + rtx op0 = operands[0]; + rtx op1 = operands[1]; + + start_sequence (); + emit_insn (gen_cmp_ltusi_insn (op1, const1_rtx)); + emit_insn (gen_movcc_insn (op0)); + operands[3] = get_insns (); + end_sequence (); +}") + +(define_insn "seq_insn" + [(set (match_operand:SI 0 "register_operand" "=r,r,??r,r") + (eq:SI (match_operand:SI 1 "register_operand" "r,r,r,r") + (match_operand:SI 2 "reg_or_eq_int16_operand" "r,r,r,PK"))) + (clobber (reg:CC 17)) + (clobber (match_scratch:SI 3 "=1,2,&r,r"))] + "TARGET_M32R" + "#" + [(set_attr "type" "multi") + (set_attr "length" "8,8,10,10")]) + +(define_split + [(set (match_operand:SI 0 "register_operand" "") + (eq:SI (match_operand:SI 1 "register_operand" "") + (match_operand:SI 2 "reg_or_eq_int16_operand" ""))) + (clobber (reg:CC 17)) + (clobber (match_scratch:SI 3 ""))] + "TARGET_M32R && reload_completed" + [(match_dup 4)] + " +{ + rtx op0 = operands[0]; + rtx op1 = operands[1]; + rtx op2 = operands[2]; + rtx op3 = operands[3]; + HOST_WIDE_INT value; + + if (REG_P (op2) && REG_P (op3) + && REGNO (op2) == REGNO (op3)) + { + op1 = operands[2]; + op2 = operands[1]; + } + + start_sequence (); + if (REG_P (op1) && REG_P (op3) + && REGNO (op1) != REGNO (op3)) + { + emit_move_insn (op3, op1); + op1 = op3; + } + + if (satisfies_constraint_P (op2) && (value = INTVAL (op2)) != 0) + emit_insn (gen_addsi3 (op3, op1, GEN_INT (-value))); + else + emit_insn (gen_xorsi3 (op3, op1, op2)); + + emit_insn (gen_cmp_ltusi_insn (op3, const1_rtx)); + emit_insn (gen_movcc_insn (op0)); + operands[4] = get_insns (); + end_sequence (); +}") + +(define_insn "sne_zero_insn" + [(set (match_operand:SI 0 "register_operand" "=r") + (ne:SI (match_operand:SI 1 "register_operand" "r") + (const_int 0))) + (clobber (reg:CC 17)) + (clobber (match_scratch:SI 2 "=&r"))] + "" + "#" + [(set_attr "type" "multi") + (set_attr "length" "6")]) + +(define_split + [(set (match_operand:SI 0 "register_operand" "") + (ne:SI (match_operand:SI 1 "register_operand" "") + (const_int 0))) + (clobber (reg:CC 17)) + (clobber (match_scratch:SI 2 ""))] + "reload_completed" + [(set (match_dup 2) + (const_int 0)) + (set (reg:CC 17) + (ltu:CC (match_dup 2) + (match_dup 1))) + (set (match_dup 0) + (ne:SI (reg:CC 17) (const_int 0)))] + "") + +(define_insn "slt_insn" + [(set (match_operand:SI 0 "register_operand" "=r,r") + (lt:SI (match_operand:SI 1 "register_operand" "r,r") + (match_operand:SI 2 "reg_or_int16_operand" "r,J"))) + (clobber (reg:CC 17))] + "" + "#" + [(set_attr "type" "multi") + (set_attr "length" "4,6")]) + +(define_split + [(set (match_operand:SI 0 "register_operand" "") + (lt:SI (match_operand:SI 1 "register_operand" "") + (match_operand:SI 2 "reg_or_int16_operand" ""))) + (clobber (reg:CC 17))] + "" + [(set (reg:CC 17) + (lt:CC (match_dup 1) + (match_dup 2))) + (set (match_dup 0) + (ne:SI (reg:CC 17) (const_int 0)))] + "") + +(define_insn "sle_insn" + [(set (match_operand:SI 0 "register_operand" "=r") + (le:SI (match_operand:SI 1 "register_operand" "r") + (match_operand:SI 2 "register_operand" "r"))) + (clobber (reg:CC 17))] + "" + "#" + [(set_attr "type" "multi") + (set_attr "length" "8")]) + +(define_split + [(set (match_operand:SI 0 "register_operand" "") + (le:SI (match_operand:SI 1 "register_operand" "") + (match_operand:SI 2 "register_operand" ""))) + (clobber (reg:CC 17))] + "!optimize_size" + [(set (reg:CC 17) + (lt:CC (match_dup 2) + (match_dup 1))) + (set (match_dup 0) + (ne:SI (reg:CC 17) (const_int 0))) + (set (match_dup 0) + (xor:SI (match_dup 0) + (const_int 1)))] + "") + +;; If optimizing for space, use -(reg - 1) to invert the comparison rather than +;; xor reg,reg,1 which might eliminate a NOP being inserted. +(define_split + [(set (match_operand:SI 0 "register_operand" "") + (le:SI (match_operand:SI 1 "register_operand" "") + (match_operand:SI 2 "register_operand" ""))) + (clobber (reg:CC 17))] + "optimize_size" + [(set (reg:CC 17) + (lt:CC (match_dup 2) + (match_dup 1))) + (set (match_dup 0) + (ne:SI (reg:CC 17) (const_int 0))) + (set (match_dup 0) + (plus:SI (match_dup 0) + (const_int -1))) + (set (match_dup 0) + (neg:SI (match_dup 0)))] + "") + +(define_insn "sge_insn" + [(set (match_operand:SI 0 "register_operand" "=r,r") + (ge:SI (match_operand:SI 1 "register_operand" "r,r") + (match_operand:SI 2 "reg_or_int16_operand" "r,J"))) + (clobber (reg:CC 17))] + "" + "#" + [(set_attr "type" "multi") + (set_attr "length" "8,10")]) + +(define_split + [(set (match_operand:SI 0 "register_operand" "") + (ge:SI (match_operand:SI 1 "register_operand" "") + (match_operand:SI 2 "reg_or_int16_operand" ""))) + (clobber (reg:CC 17))] + "!optimize_size" + [(set (reg:CC 17) + (lt:CC (match_dup 1) + (match_dup 2))) + (set (match_dup 0) + (ne:SI (reg:CC 17) (const_int 0))) + (set (match_dup 0) + (xor:SI (match_dup 0) + (const_int 1)))] + "") + +;; If optimizing for space, use -(reg - 1) to invert the comparison rather than +;; xor reg,reg,1 which might eliminate a NOP being inserted. +(define_split + [(set (match_operand:SI 0 "register_operand" "") + (ge:SI (match_operand:SI 1 "register_operand" "") + (match_operand:SI 2 "reg_or_int16_operand" ""))) + (clobber (reg:CC 17))] + "optimize_size" + [(set (reg:CC 17) + (lt:CC (match_dup 1) + (match_dup 2))) + (set (match_dup 0) + (ne:SI (reg:CC 17) (const_int 0))) + (set (match_dup 0) + (plus:SI (match_dup 0) + (const_int -1))) + (set (match_dup 0) + (neg:SI (match_dup 0)))] + "") + +(define_insn "sltu_insn" + [(set (match_operand:SI 0 "register_operand" "=r,r") + (ltu:SI (match_operand:SI 1 "register_operand" "r,r") + (match_operand:SI 2 "reg_or_int16_operand" "r,J"))) + (clobber (reg:CC 17))] + "" + "#" + [(set_attr "type" "multi") + (set_attr "length" "6,8")]) + +(define_split + [(set (match_operand:SI 0 "register_operand" "") + (ltu:SI (match_operand:SI 1 "register_operand" "") + (match_operand:SI 2 "reg_or_int16_operand" ""))) + (clobber (reg:CC 17))] + "" + [(set (reg:CC 17) + (ltu:CC (match_dup 1) + (match_dup 2))) + (set (match_dup 0) + (ne:SI (reg:CC 17) (const_int 0)))] + "") + +(define_insn "sleu_insn" + [(set (match_operand:SI 0 "register_operand" "=r") + (leu:SI (match_operand:SI 1 "register_operand" "r") + (match_operand:SI 2 "register_operand" "r"))) + (clobber (reg:CC 17))] + "" + "#" + [(set_attr "type" "multi") + (set_attr "length" "8")]) + +(define_split + [(set (match_operand:SI 0 "register_operand" "") + (leu:SI (match_operand:SI 1 "register_operand" "") + (match_operand:SI 2 "register_operand" ""))) + (clobber (reg:CC 17))] + "!optimize_size" + [(set (reg:CC 17) + (ltu:CC (match_dup 2) + (match_dup 1))) + (set (match_dup 0) + (ne:SI (reg:CC 17) (const_int 0))) + (set (match_dup 0) + (xor:SI (match_dup 0) + (const_int 1)))] + "") + +;; If optimizing for space, use -(reg - 1) to invert the comparison rather than +;; xor reg,reg,1 which might eliminate a NOP being inserted. +(define_split + [(set (match_operand:SI 0 "register_operand" "") + (leu:SI (match_operand:SI 1 "register_operand" "") + (match_operand:SI 2 "register_operand" ""))) + (clobber (reg:CC 17))] + "optimize_size" + [(set (reg:CC 17) + (ltu:CC (match_dup 2) + (match_dup 1))) + (set (match_dup 0) + (ne:SI (reg:CC 17) (const_int 0))) + (set (match_dup 0) + (plus:SI (match_dup 0) + (const_int -1))) + (set (match_dup 0) + (neg:SI (match_dup 0)))] + "") + +(define_insn "sgeu_insn" + [(set (match_operand:SI 0 "register_operand" "=r,r") + (geu:SI (match_operand:SI 1 "register_operand" "r,r") + (match_operand:SI 2 "reg_or_int16_operand" "r,J"))) + (clobber (reg:CC 17))] + "" + "#" + [(set_attr "type" "multi") + (set_attr "length" "8,10")]) + +(define_split + [(set (match_operand:SI 0 "register_operand" "") + (geu:SI (match_operand:SI 1 "register_operand" "") + (match_operand:SI 2 "reg_or_int16_operand" ""))) + (clobber (reg:CC 17))] + "!optimize_size" + [(set (reg:CC 17) + (ltu:CC (match_dup 1) + (match_dup 2))) + (set (match_dup 0) + (ne:SI (reg:CC 17) (const_int 0))) + (set (match_dup 0) + (xor:SI (match_dup 0) + (const_int 1)))] + "") + +;; If optimizing for space, use -(reg - 1) to invert the comparison rather than +;; xor reg,reg,1 which might eliminate a NOP being inserted. +(define_split + [(set (match_operand:SI 0 "register_operand" "") + (geu:SI (match_operand:SI 1 "register_operand" "") + (match_operand:SI 2 "reg_or_int16_operand" ""))) + (clobber (reg:CC 17))] + "optimize_size" + [(set (reg:CC 17) + (ltu:CC (match_dup 1) + (match_dup 2))) + (set (match_dup 0) + (ne:SI (reg:CC 17) (const_int 0))) + (set (match_dup 0) + (plus:SI (match_dup 0) + (const_int -1))) + (set (match_dup 0) + (neg:SI (match_dup 0)))] + "") + +(define_insn "movcc_insn" + [(set (match_operand:SI 0 "register_operand" "=r") + (ne:SI (reg:CC 17) (const_int 0)))] + "" + "mvfc %0, cbr" + [(set_attr "type" "misc") + (set_attr "length" "2")]) + + +;; Unconditional and other jump instructions. + +(define_insn "jump" + [(set (pc) (label_ref (match_operand 0 "" "")))] + "" + "bra %l0" + [(set_attr "type" "uncond_branch") + (set (attr "length") (if_then_else (ltu (plus (minus (match_dup 0) (pc)) + (const_int 400)) + (const_int 800)) + (const_int 2) + (const_int 4)))]) + +(define_insn "indirect_jump" + [(set (pc) (match_operand:SI 0 "address_operand" "p"))] + "" + "jmp %a0" + [(set_attr "type" "uncond_branch") + (set_attr "length" "2")]) + +(define_insn "return_lr" + [(parallel [(return) (use (reg:SI 14))])] + "" + "jmp lr" + [(set_attr "type" "uncond_branch") + (set_attr "length" "2")]) + +(define_insn "return_rte" + [(return)] + "" + "rte" + [(set_attr "type" "uncond_branch") + (set_attr "length" "2")]) + +(define_expand "return" + [(return)] + "direct_return ()" + " +{ + emit_jump_insn (gen_return_lr ()); + DONE; +}") + +(define_expand "return_normal" + [(return)] + "!direct_return ()" + " +{ + enum m32r_function_type fn_type; + + fn_type = m32r_compute_function_type (current_function_decl); + if (M32R_INTERRUPT_P (fn_type)) + { + emit_jump_insn (gen_return_rte ()); + DONE; + } + + emit_jump_insn (gen_return_lr ()); + DONE; +}") + +(define_expand "tablejump" + [(parallel [(set (pc) (match_operand 0 "register_operand" "r")) + (use (label_ref (match_operand 1 "" "")))])] + "" + " +{ + /* In pic mode, our address differences are against the base of the + table. Add that base value back in; CSE ought to be able to combine + the two address loads. */ + if (flag_pic) + { + rtx tmp, tmp2; + + tmp = gen_rtx_LABEL_REF (Pmode, operands[1]); + tmp2 = operands[0]; + tmp = gen_rtx_PLUS (Pmode, tmp2, tmp); + operands[0] = memory_address (Pmode, tmp); + } +}") + +(define_insn "*tablejump_insn" + [(set (pc) (match_operand:SI 0 "address_operand" "p")) + (use (label_ref (match_operand 1 "" "")))] + "" + "jmp %a0" + [(set_attr "type" "uncond_branch") + (set_attr "length" "2")]) + +(define_expand "call" + ;; operands[1] is stack_size_rtx + ;; operands[2] is next_arg_register + [(parallel [(call (match_operand:SI 0 "call_operand" "") + (match_operand 1 "" "")) + (clobber (reg:SI 14))])] + "" + " +{ + if (flag_pic) + crtl->uses_pic_offset_table = 1; +}") + +(define_insn "*call_via_reg" + [(call (mem:SI (match_operand:SI 0 "register_operand" "r")) + (match_operand 1 "" "")) + (clobber (reg:SI 14))] + "" + "jl %0" + [(set_attr "type" "call") + (set_attr "length" "2")]) + +(define_insn "*call_via_label" + [(call (mem:SI (match_operand:SI 0 "call_address_operand" "")) + (match_operand 1 "" "")) + (clobber (reg:SI 14))] + "" + "* +{ + int call26_p = call26_operand (operands[0], FUNCTION_MODE); + + if (! call26_p) + { + /* We may not be able to reach with a `bl' insn so punt and leave it to + the linker. + We do this here, rather than doing a force_reg in the define_expand + so these insns won't be separated, say by scheduling, thus simplifying + the linker. */ + return \"seth r14,%T0\;add3 r14,r14,%B0\;jl r14\"; + } + else + return \"bl %0\"; +}" + [(set_attr "type" "call") + (set (attr "length") + (if_then_else (not (match_test "call26_operand (operands[0], FUNCTION_MODE)")) + (const_int 12) ; 10 + 2 for nop filler + ; The return address must be on a 4 byte boundary so + ; there's no point in using a value of 2 here. A 2 byte + ; insn may go in the left slot but we currently can't + ; use such knowledge. + (const_int 4)))]) + +(define_expand "call_value" + ;; operand 2 is stack_size_rtx + ;; operand 3 is next_arg_register + [(parallel [(set (match_operand 0 "register_operand" "=r") + (call (match_operand:SI 1 "call_operand" "") + (match_operand 2 "" ""))) + (clobber (reg:SI 14))])] + "" + " +{ + if (flag_pic) + crtl->uses_pic_offset_table = 1; +}") + +(define_insn "*call_value_via_reg" + [(set (match_operand 0 "register_operand" "=r") + (call (mem:SI (match_operand:SI 1 "register_operand" "r")) + (match_operand 2 "" ""))) + (clobber (reg:SI 14))] + "" + "jl %1" + [(set_attr "type" "call") + (set_attr "length" "2")]) + +(define_insn "*call_value_via_label" + [(set (match_operand 0 "register_operand" "=r") + (call (mem:SI (match_operand:SI 1 "call_address_operand" "")) + (match_operand 2 "" ""))) + (clobber (reg:SI 14))] + "" + "* +{ + int call26_p = call26_operand (operands[1], FUNCTION_MODE); + + if (flag_pic) + crtl->uses_pic_offset_table = 1; + + if (! call26_p) + { + /* We may not be able to reach with a `bl' insn so punt and leave it to + the linker. + We do this here, rather than doing a force_reg in the define_expand + so these insns won't be separated, say by scheduling, thus simplifying + the linker. */ + return \"seth r14,%T1\;add3 r14,r14,%B1\;jl r14\"; + } + else + return \"bl %1\"; +}" + [(set_attr "type" "call") + (set (attr "length") + (if_then_else (not (match_test "call26_operand (operands[1], FUNCTION_MODE)")) + (const_int 12) ; 10 + 2 for nop filler + ; The return address must be on a 4 byte boundary so + ; there's no point in using a value of 2 here. A 2 byte + ; insn may go in the left slot but we currently can't + ; use such knowledge. + (const_int 4)))]) + +(define_insn "nop" + [(const_int 0)] + "" + "nop" + [(set_attr "type" "int2") + (set_attr "length" "2")]) + +;; UNSPEC_VOLATILE is considered to use and clobber all hard registers and +;; all of memory. This blocks insns from being moved across this point. + +(define_insn "blockage" + [(unspec_volatile [(const_int 0)] UNSPECV_BLOCKAGE)] + "" + "") + +;; Special pattern to flush the icache. + +(define_insn "flush_icache" + [(unspec_volatile [(match_operand 0 "memory_operand" "m")] + UNSPECV_FLUSH_ICACHE) + (match_operand 1 "" "") + (clobber (reg:SI 17))] + "" + "* return \"trap %#%1 ; flush-icache\";" + [(set_attr "type" "int4") + (set_attr "length" "4")]) + +;; Speed up fabs and provide correct sign handling for -0 + +(define_insn "absdf2" + [(set (match_operand:DF 0 "register_operand" "=r") + (abs:DF (match_operand:DF 1 "register_operand" "0")))] + "" + "#" + [(set_attr "type" "multi") + (set_attr "length" "4")]) + +(define_split + [(set (match_operand:DF 0 "register_operand" "") + (abs:DF (match_operand:DF 1 "register_operand" "")))] + "reload_completed" + [(set (match_dup 2) + (ashift:SI (match_dup 2) + (const_int 1))) + (set (match_dup 2) + (lshiftrt:SI (match_dup 2) + (const_int 1)))] + "operands[2] = gen_highpart (SImode, operands[0]);") + +(define_insn "abssf2" + [(set (match_operand:SF 0 "register_operand" "=r") + (abs:SF (match_operand:SF 1 "register_operand" "0")))] + "" + "#" + [(set_attr "type" "multi") + (set_attr "length" "4")]) + +(define_split + [(set (match_operand:SF 0 "register_operand" "") + (abs:SF (match_operand:SF 1 "register_operand" "")))] + "reload_completed" + [(set (match_dup 2) + (ashift:SI (match_dup 2) + (const_int 1))) + (set (match_dup 2) + (lshiftrt:SI (match_dup 2) + (const_int 1)))] + "operands[2] = gen_highpart (SImode, operands[0]);") + +;; Conditional move instructions +;; Based on those done for the d10v + +(define_expand "movsicc" + [ + (set (match_operand:SI 0 "register_operand" "r") + (if_then_else:SI (match_operand 1 "" "") + (match_operand:SI 2 "conditional_move_operand" "O") + (match_operand:SI 3 "conditional_move_operand" "O") + ) + ) + ] + "" + " +{ + if (! zero_and_one (operands [2], operands [3])) + FAIL; + + /* Generate the comparison that will set the carry flag. */ + operands[1] = gen_compare (GET_CODE (operands[1]), XEXP (operands[1], 0), + XEXP (operands[1], 1), TRUE); + + /* See other movsicc pattern below for reason why. */ + emit_insn (gen_blockage ()); +}") + +;; Generate the conditional instructions based on how the carry flag is examined. +(define_insn "*movsicc_internal" + [(set (match_operand:SI 0 "register_operand" "=r") + (if_then_else:SI (match_operand 1 "carry_compare_operand" "") + (match_operand:SI 2 "conditional_move_operand" "O") + (match_operand:SI 3 "conditional_move_operand" "O") + ) + )] + "zero_and_one (operands [2], operands[3])" + "* return emit_cond_move (operands, insn);" + [(set_attr "type" "multi") + (set_attr "length" "8") + ] +) + + +;; Block moves, see m32r.c for more details. +;; Argument 0 is the destination +;; Argument 1 is the source +;; Argument 2 is the length +;; Argument 3 is the alignment + +(define_expand "movmemsi" + [(parallel [(set (match_operand:BLK 0 "general_operand" "") + (match_operand:BLK 1 "general_operand" "")) + (use (match_operand:SI 2 "immediate_operand" "")) + (use (match_operand:SI 3 "immediate_operand" ""))])] + "" + " +{ + if (operands[0]) /* Avoid unused code messages. */ + { + if (m32r_expand_block_move (operands)) + DONE; + else + FAIL; + } +}") + +;; Insn generated by block moves + +(define_insn "movmemsi_internal" + [(set (mem:BLK (match_operand:SI 0 "register_operand" "r")) ;; destination + (mem:BLK (match_operand:SI 1 "register_operand" "r"))) ;; source + (use (match_operand:SI 2 "m32r_block_immediate_operand" "J"));; # bytes to move + (set (match_operand:SI 3 "register_operand" "=0") + (plus:SI (minus (match_dup 2) (const_int 4)) + (match_dup 0))) + (set (match_operand:SI 4 "register_operand" "=1") + (plus:SI (match_dup 1) + (match_dup 2))) + (clobber (match_scratch:SI 5 "=&r")) ;; temp1 + (clobber (match_scratch:SI 6 "=&r"))] ;; temp2 + "" + "* m32r_output_block_move (insn, operands); return \"\"; " + [(set_attr "type" "store8") + (set_attr "length" "72")]) ;; Maximum + +;; PIC + +/* When generating pic, we need to load the symbol offset into a register. + So that the optimizer does not confuse this with a normal symbol load + we use an unspec. The offset will be loaded from a constant pool entry, + since that is the only type of relocation we can use. */ + +(define_insn "pic_load_addr" + [(set (match_operand:SI 0 "register_operand" "=r") + (unspec:SI [(match_operand 1 "" "")] UNSPEC_PIC_LOAD_ADDR))] + "flag_pic" + "ld24 %0,%#%1" + [(set_attr "type" "int4")]) + +(define_insn "gotoff_load_addr" + [(set (match_operand:SI 0 "register_operand" "=r") + (unspec:SI [(match_operand 1 "" "")] UNSPEC_GOTOFF))] + "flag_pic" + "seth %0, %#shigh(%1@GOTOFF)\;add3 %0, %0, low(%1@GOTOFF)" + [(set_attr "type" "int4") + (set_attr "length" "8")]) + +;; Load program counter insns. + +(define_insn "get_pc" + [(clobber (reg:SI 14)) + (set (match_operand 0 "register_operand" "=r,r") + (unspec [(match_operand 1 "" "")] UNSPEC_GET_PC)) + (use (match_operand:SI 2 "immediate_operand" "W,i"))] + "flag_pic" + "@ + bl.s .+4\;seth %0,%#shigh(%1)\;add3 %0,%0,%#low(%1+4)\;add %0,lr + bl.s .+4\;ld24 %0,%#%1\;add %0,lr" + [(set_attr "length" "12,8")]) + +(define_expand "builtin_setjmp_receiver" + [(label_ref (match_operand 0 "" ""))] + "flag_pic" + " +{ + m32r_load_pic_register (); + DONE; +}") -- cgit v1.2.3