;; Machine description of the Renesas M32R cpu for GNU C compiler ;; Copyright (C) 1996-2022 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") ) ) ] "" " { /* FIXME: This expansion is hold over from a failed conversion of this port away from using cc0. It still relies upon the last comparison being the one that is now tested. Disabled for now in order to improve the generation of working code. */ FAIL; 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.cc 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 "cpymemsi" [(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 "cpymemsi_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; }")