;; Machine description of the Argonaut ARC cpu for GNU C compiler ;; Copyright (C) 1994, 1997, 1998, 1999, 2000, 2004, 2005, 2007, 2008 ;; 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. ;; ??? This is an old port, and is undoubtedly suffering from bit rot. ;; Insn type. Used to default other attribute values. (define_attr "type" "move,load,store,cmove,unary,binary,compare,shift,mul,uncond_branch,branch,call,call_no_delay_slot,multi,misc" (const_string "binary")) ;; Length (in # of insns, long immediate constants counted too). ;; ??? There's a nasty interaction between the conditional execution fsm ;; and insn lengths: insns with shimm values cannot be conditionally executed. (define_attr "length" "" (cond [(eq_attr "type" "load") (if_then_else (match_operand 1 "long_immediate_loadstore_operand" "") (const_int 2) (const_int 1)) (eq_attr "type" "store") (if_then_else (match_operand 0 "long_immediate_loadstore_operand" "") (const_int 2) (const_int 1)) (eq_attr "type" "move,unary,compare") (if_then_else (match_operand 1 "long_immediate_operand" "") (const_int 2) (const_int 1)) (eq_attr "type" "binary,mul") (if_then_else (match_operand 2 "long_immediate_operand" "") (const_int 2) (const_int 1)) (eq_attr "type" "cmove") (if_then_else (match_operand 2 "register_operand" "") (const_int 1) (const_int 2)) (eq_attr "type" "multi") (const_int 2) ] (const_int 1))) ;; The length here is the length of a single asm. Unfortunately it might be ;; 1 or 2 so we must allow for 2. That's ok though. How often will users ;; lament asm's not being put in delay slots? (define_asm_attributes [(set_attr "length" "2") (set_attr "type" "multi")]) ;; Condition codes: this one is used by final_prescan_insn to speed up ;; conditionalizing instructions. It saves having to scan the rtl to see if ;; it uses or alters the condition codes. ;; USE: This insn uses the condition codes (e.g.: a conditional branch). ;; CANUSE: This insn can use the condition codes (for conditional execution). ;; SET: All condition codes are set by this insn. ;; SET_ZN: the Z and N flags are set by this insn. ;; SET_ZNC: the Z, N, and C flags are set by this insn. ;; CLOB: The condition codes are set to unknown values by this insn. ;; NOCOND: This insn can't use and doesn't affect the condition codes. (define_attr "cond" "use,canuse,set,set_zn,set_znc,clob,nocond" (cond [(and (eq_attr "type" "unary,binary,move") (eq_attr "length" "1")) (const_string "canuse") (eq_attr "type" "compare") (const_string "set") (eq_attr "type" "cmove,branch") (const_string "use") (eq_attr "type" "multi,misc") (const_string "clob") ] (const_string "nocond"))) ;; Delay slots. (define_attr "in_delay_slot" "false,true" (cond [(eq_attr "type" "uncond_branch,branch,call,call_no_delay_slot,multi") (const_string "false") ] (if_then_else (eq_attr "length" "1") (const_string "true") (const_string "false")))) (define_delay (eq_attr "type" "call") [(eq_attr "in_delay_slot" "true") (eq_attr "in_delay_slot" "true") (eq_attr "in_delay_slot" "true")]) (define_delay (eq_attr "type" "branch,uncond_branch") [(eq_attr "in_delay_slot" "true") (eq_attr "in_delay_slot" "true") (eq_attr "in_delay_slot" "true")]) ;; Scheduling description for the ARC (define_cpu_unit "branch") (define_insn_reservation "any_insn" 1 (eq_attr "type" "!load,compare,branch") "nothing") ;; 1) A conditional jump cannot immediately follow the insn setting the flags. ;; This isn't a complete solution as it doesn't come with guarantees. That ;; is done in the branch patterns and in arc_print_operand. This exists to ;; avoid inserting a nop when we can. (define_insn_reservation "compare" 1 (eq_attr "type" "compare") "nothing,branch") (define_insn_reservation "branch" 1 (eq_attr "type" "branch") "branch") ;; 2) References to loaded registers should wait a cycle. ;; Memory with load-delay of 1 (i.e., 2 cycle load). (define_insn_reservation "memory" 2 (eq_attr "type" "load") "nothing") ;; Move instructions. (define_expand "movqi" [(set (match_operand:QI 0 "general_operand" "") (match_operand:QI 1 "general_operand" ""))] "" " { /* Everything except mem = const or mem = mem can be done easily. */ if (GET_CODE (operands[0]) == MEM) operands[1] = force_reg (QImode, operands[1]); }") (define_insn "*movqi_insn" [(set (match_operand:QI 0 "move_dest_operand" "=r,r,r,m") (match_operand:QI 1 "move_src_operand" "rI,Ji,m,r"))] ;; ??? Needed? "register_operand (operands[0], QImode) || register_operand (operands[1], QImode)" "@ mov%? %0,%1 mov%? %0,%1 ldb%U1%V1 %0,%1 stb%U0%V0 %1,%0" [(set_attr "type" "move,move,load,store")]) ;; ??? This may never match since there's no cmpqi insn. (define_insn "*movqi_set_cc_insn" [(set (reg:CCZN 61) (compare:CCZN (sign_extend:SI (match_operand:QI 1 "move_src_operand" "rIJi")) (const_int 0))) (set (match_operand:QI 0 "move_dest_operand" "=r") (match_dup 1))] "" "mov%?.f %0,%1" [(set_attr "type" "move") (set_attr "cond" "set_zn")]) (define_expand "movhi" [(set (match_operand:HI 0 "general_operand" "") (match_operand:HI 1 "general_operand" ""))] "" " { /* Everything except mem = const or mem = mem can be done easily. */ if (GET_CODE (operands[0]) == MEM) operands[1] = force_reg (HImode, operands[1]); }") (define_insn "*movhi_insn" [(set (match_operand:HI 0 "move_dest_operand" "=r,r,r,m") (match_operand:HI 1 "move_src_operand" "rI,Ji,m,r"))] "register_operand (operands[0], HImode) || register_operand (operands[1], HImode)" "@ mov%? %0,%1 mov%? %0,%1 ldw%U1%V1 %0,%1 stw%U0%V0 %1,%0" [(set_attr "type" "move,move,load,store")]) ;; ??? Will this ever match? (define_insn "*movhi_set_cc_insn" [(set (reg:CCZN 61) (compare:CCZN (sign_extend:SI (match_operand:HI 1 "move_src_operand" "rIJi")) (const_int 0))) (set (match_operand:HI 0 "move_dest_operand" "=r") (match_dup 1))] ;; ??? Needed? "register_operand (operands[0], HImode) || register_operand (operands[1], HImode)" "mov%?.f %0,%1" [(set_attr "type" "move") (set_attr "cond" "set_zn")]) (define_expand "movsi" [(set (match_operand:SI 0 "general_operand" "") (match_operand:SI 1 "general_operand" ""))] "" " { /* Everything except mem = const or mem = mem can be done easily. */ if (GET_CODE (operands[0]) == MEM) operands[1] = force_reg (SImode, operands[1]); }") (define_insn "*movsi_insn" [(set (match_operand:SI 0 "move_dest_operand" "=r,r,r,m") (match_operand:SI 1 "move_src_operand" "rI,GJi,m,r"))] "register_operand (operands[0], SImode) || register_operand (operands[1], SImode)" "@ mov%? %0,%1 mov%? %0,%S1 ld%U1%V1 %0,%1 st%U0%V0 %1,%0" [(set_attr "type" "move,move,load,store")]) (define_insn "*movsi_set_cc_insn" [(set (reg:CCZN 61) (compare:CCZN (match_operand:SI 1 "move_src_operand" "rIJi") (const_int 0))) (set (match_operand:SI 0 "move_dest_operand" "=r") (match_dup 1))] "register_operand (operands[0], SImode) || register_operand (operands[1], SImode)" "mov%?.f %0,%S1" [(set_attr "type" "move") (set_attr "cond" "set_zn")]) (define_expand "movdi" [(set (match_operand:DI 0 "general_operand" "") (match_operand:DI 1 "general_operand" ""))] "" " { /* Everything except mem = const or mem = mem can be done easily. */ if (GET_CODE (operands[0]) == MEM) operands[1] = force_reg (DImode, operands[1]); }") (define_insn "*movdi_insn" [(set (match_operand:DI 0 "move_dest_operand" "=r,r,r,m") (match_operand:DI 1 "move_double_src_operand" "r,HK,m,r"))] "register_operand (operands[0], DImode) || register_operand (operands[1], DImode)" "* { switch (which_alternative) { case 0 : /* We normally copy the low-numbered register first. However, if the first register operand 0 is the same as the second register of operand 1, we must copy in the opposite order. */ if (REGNO (operands[0]) == REGNO (operands[1]) + 1) return \"mov %R0,%R1\;mov %0,%1\"; else return \"mov %0,%1\;mov %R0,%R1\"; case 1 : return \"mov %0,%L1\;mov %R0,%H1\"; case 2 : /* If the low-address word is used in the address, we must load it last. Otherwise, load it first. Note that we cannot have auto-increment in that case since the address register is known to be dead. */ if (refers_to_regno_p (REGNO (operands[0]), REGNO (operands[0]) + 1, operands [1], 0)) return \"ld%V1 %R0,%R1\;ld%V1 %0,%1\"; else return \"ld%V1 %0,%1\;ld%V1 %R0,%R1\"; case 3 : return \"st%V0 %1,%0\;st%V0 %R1,%R0\"; default: gcc_unreachable (); } }" [(set_attr "type" "move,move,load,store") ;; ??? The ld/st values could be 4 if it's [reg,bignum]. (set_attr "length" "2,4,2,2")]) ;(define_expand "movdi" ; [(set (match_operand:DI 0 "general_operand" "") ; (match_operand:DI 1 "general_operand" ""))] ; "" ; " ;{ ; /* Flow doesn't understand that this is effectively a DFmode move. ; It doesn't know that all of `operands[0]' is set. */ ; emit_clobber (operands[0]); ; ; /* Emit insns that movsi_insn can handle. */ ; emit_insn (gen_movsi (operand_subword (operands[0], 0, 0, DImode), ; operand_subword (operands[1], 0, 0, DImode))); ; emit_insn (gen_movsi (operand_subword (operands[0], 1, 0, DImode), ; operand_subword (operands[1], 1, 0, DImode))); ; DONE; ;}") ;; Floating point move insns. (define_expand "movsf" [(set (match_operand:SF 0 "general_operand" "") (match_operand:SF 1 "general_operand" ""))] "" " { /* Everything except mem = const or mem = mem can be done easily. */ if (GET_CODE (operands[0]) == MEM) operands[1] = force_reg (SFmode, operands[1]); }") (define_insn "*movsf_insn" [(set (match_operand:SF 0 "move_dest_operand" "=r,r,r,m") (match_operand:SF 1 "move_src_operand" "r,E,m,r"))] "register_operand (operands[0], SFmode) || register_operand (operands[1], SFmode)" "@ mov%? %0,%1 mov%? %0,%1 ; %A1 ld%U1%V1 %0,%1 st%U0%V0 %1,%0" [(set_attr "type" "move,move,load,store")]) (define_expand "movdf" [(set (match_operand:DF 0 "general_operand" "") (match_operand:DF 1 "general_operand" ""))] "" " { /* Everything except mem = const or mem = mem can be done easily. */ if (GET_CODE (operands[0]) == MEM) 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,E,m,r"))] "register_operand (operands[0], DFmode) || register_operand (operands[1], DFmode)" "* { switch (which_alternative) { case 0 : /* We normally copy the low-numbered register first. However, if the first register operand 0 is the same as the second register of operand 1, we must copy in the opposite order. */ if (REGNO (operands[0]) == REGNO (operands[1]) + 1) return \"mov %R0,%R1\;mov %0,%1\"; else return \"mov %0,%1\;mov %R0,%R1\"; case 1 : return \"mov %0,%L1\;mov %R0,%H1 ; %A1\"; case 2 : /* If the low-address word is used in the address, we must load it last. Otherwise, load it first. Note that we cannot have auto-increment in that case since the address register is known to be dead. */ if (refers_to_regno_p (REGNO (operands[0]), REGNO (operands[0]) + 1, operands [1], 0)) return \"ld%V1 %R0,%R1\;ld%V1 %0,%1\"; else return \"ld%V1 %0,%1\;ld%V1 %R0,%R1\"; case 3 : return \"st%V0 %1,%0\;st%V0 %R1,%R0\"; default: gcc_unreachable (); } }" [(set_attr "type" "move,move,load,store") ;; ??? The ld/st values could be 4 if it's [reg,bignum]. (set_attr "length" "2,4,2,2")]) ;(define_expand "movdf" ; [(set (match_operand:DF 0 "general_operand" "") ; (match_operand:DF 1 "general_operand" ""))] ; "" ; " ;{ ; /* Flow doesn't understand that this is effectively a DFmode move. ; It doesn't know that all of `operands[0]' is set. */ ; emit_clobber (operands[0]); ; ; /* Emit insns that movsi_insn can handle. */ ; emit_insn (gen_movsi (operand_subword (operands[0], 0, 0, DFmode), ; operand_subword (operands[1], 0, 0, DFmode))); ; emit_insn (gen_movsi (operand_subword (operands[0], 1, 0, DFmode), ; operand_subword (operands[1], 1, 0, DFmode))); ; DONE; ;}") ;; Load/Store with update instructions. ;; ;; Some of these we can get by using pre-decrement or pre-increment, but the ;; hardware can also do cases where the increment is not the size of the ;; object. ;; ;; In all these cases, we use operands 0 and 1 for the register being ;; incremented because those are the operands that local-alloc will ;; tie and these are the pair most likely to be tieable (and the ones ;; that will benefit the most). ;; ;; We use match_operator here because we need to know whether the memory ;; object is volatile or not. (define_insn "*loadqi_update" [(set (match_operand:QI 3 "register_operand" "=r,r") (match_operator:QI 4 "load_update_operand" [(match_operand:SI 1 "register_operand" "0,0") (match_operand:SI 2 "nonmemory_operand" "rI,J")])) (set (match_operand:SI 0 "register_operand" "=r,r") (plus:SI (match_dup 1) (match_dup 2)))] "" "ldb.a%V4 %3,[%0,%2]" [(set_attr "type" "load,load") (set_attr "length" "1,2")]) (define_insn "*load_zeroextendqisi_update" [(set (match_operand:SI 3 "register_operand" "=r,r") (zero_extend:SI (match_operator:QI 4 "load_update_operand" [(match_operand:SI 1 "register_operand" "0,0") (match_operand:SI 2 "nonmemory_operand" "rI,J")]))) (set (match_operand:SI 0 "register_operand" "=r,r") (plus:SI (match_dup 1) (match_dup 2)))] "" "ldb.a%V4 %3,[%0,%2]" [(set_attr "type" "load,load") (set_attr "length" "1,2")]) (define_insn "*load_signextendqisi_update" [(set (match_operand:SI 3 "register_operand" "=r,r") (sign_extend:SI (match_operator:QI 4 "load_update_operand" [(match_operand:SI 1 "register_operand" "0,0") (match_operand:SI 2 "nonmemory_operand" "rI,J")]))) (set (match_operand:SI 0 "register_operand" "=r,r") (plus:SI (match_dup 1) (match_dup 2)))] "" "ldb.x.a%V4 %3,[%0,%2]" [(set_attr "type" "load,load") (set_attr "length" "1,2")]) (define_insn "*storeqi_update" [(set (match_operator:QI 4 "store_update_operand" [(match_operand:SI 1 "register_operand" "0") (match_operand:SI 2 "short_immediate_operand" "I")]) (match_operand:QI 3 "register_operand" "r")) (set (match_operand:SI 0 "register_operand" "=r") (plus:SI (match_dup 1) (match_dup 2)))] "" "stb.a%V4 %3,[%0,%2]" [(set_attr "type" "store") (set_attr "length" "1")]) (define_insn "*loadhi_update" [(set (match_operand:HI 3 "register_operand" "=r,r") (match_operator:HI 4 "load_update_operand" [(match_operand:SI 1 "register_operand" "0,0") (match_operand:SI 2 "nonmemory_operand" "rI,J")])) (set (match_operand:SI 0 "register_operand" "=r,r") (plus:SI (match_dup 1) (match_dup 2)))] "" "ldw.a%V4 %3,[%0,%2]" [(set_attr "type" "load,load") (set_attr "length" "1,2")]) (define_insn "*load_zeroextendhisi_update" [(set (match_operand:SI 3 "register_operand" "=r,r") (zero_extend:SI (match_operator:HI 4 "load_update_operand" [(match_operand:SI 1 "register_operand" "0,0") (match_operand:SI 2 "nonmemory_operand" "rI,J")]))) (set (match_operand:SI 0 "register_operand" "=r,r") (plus:SI (match_dup 1) (match_dup 2)))] "" "ldw.a%V4 %3,[%0,%2]" [(set_attr "type" "load,load") (set_attr "length" "1,2")]) (define_insn "*load_signextendhisi_update" [(set (match_operand:SI 3 "register_operand" "=r,r") (sign_extend:SI (match_operator:HI 4 "load_update_operand" [(match_operand:SI 1 "register_operand" "0,0") (match_operand:SI 2 "nonmemory_operand" "rI,J")]))) (set (match_operand:SI 0 "register_operand" "=r,r") (plus:SI (match_dup 1) (match_dup 2)))] "" "ldw.x.a%V4 %3,[%0,%2]" [(set_attr "type" "load,load") (set_attr "length" "1,2")]) (define_insn "*storehi_update" [(set (match_operator:HI 4 "store_update_operand" [(match_operand:SI 1 "register_operand" "0") (match_operand:SI 2 "short_immediate_operand" "I")]) (match_operand:HI 3 "register_operand" "r")) (set (match_operand:SI 0 "register_operand" "=r") (plus:SI (match_dup 1) (match_dup 2)))] "" "stw.a%V4 %3,[%0,%2]" [(set_attr "type" "store") (set_attr "length" "1")]) (define_insn "*loadsi_update" [(set (match_operand:SI 3 "register_operand" "=r,r") (match_operator:SI 4 "load_update_operand" [(match_operand:SI 1 "register_operand" "0,0") (match_operand:SI 2 "nonmemory_operand" "rI,J")])) (set (match_operand:SI 0 "register_operand" "=r,r") (plus:SI (match_dup 1) (match_dup 2)))] "" "ld.a%V4 %3,[%0,%2]" [(set_attr "type" "load,load") (set_attr "length" "1,2")]) (define_insn "*storesi_update" [(set (match_operator:SI 4 "store_update_operand" [(match_operand:SI 1 "register_operand" "0") (match_operand:SI 2 "short_immediate_operand" "I")]) (match_operand:SI 3 "register_operand" "r")) (set (match_operand:SI 0 "register_operand" "=r") (plus:SI (match_dup 1) (match_dup 2)))] "" "st.a%V4 %3,[%0,%2]" [(set_attr "type" "store") (set_attr "length" "1")]) (define_insn "*loadsf_update" [(set (match_operand:SF 3 "register_operand" "=r,r") (match_operator:SF 4 "load_update_operand" [(match_operand:SI 1 "register_operand" "0,0") (match_operand:SI 2 "nonmemory_operand" "rI,J")])) (set (match_operand:SI 0 "register_operand" "=r,r") (plus:SI (match_dup 1) (match_dup 2)))] "" "ld.a%V4 %3,[%0,%2]" [(set_attr "type" "load,load") (set_attr "length" "1,2")]) (define_insn "*storesf_update" [(set (match_operator:SF 4 "store_update_operand" [(match_operand:SI 1 "register_operand" "0") (match_operand:SI 2 "short_immediate_operand" "I")]) (match_operand:SF 3 "register_operand" "r")) (set (match_operand:SI 0 "register_operand" "=r") (plus:SI (match_dup 1) (match_dup 2)))] "" "st.a%V4 %3,[%0,%2]" [(set_attr "type" "store") (set_attr "length" "1")]) ;; Conditional move instructions. (define_expand "movsicc" [(set (match_operand:SI 0 "register_operand" "") (if_then_else:SI (match_operand 1 "comparison_operator" "") (match_operand:SI 2 "nonmemory_operand" "") (match_operand:SI 3 "register_operand" "")))] "" " { enum rtx_code code = GET_CODE (operands[1]); rtx cc_reg = gen_compare_reg (code, XEXP (operands[1], 0), XEXP (operands[1], 1)); operands[1] = gen_rtx_fmt_ee (code, VOIDmode, cc_reg, const0_rtx); }") (define_expand "movsfcc" [(set (match_operand:SF 0 "register_operand" "") (if_then_else:SF (match_operand 1 "comparison_operator" "") (match_operand:SF 2 "nonmemory_operand" "") (match_operand:SF 3 "register_operand" "")))] "" " { enum rtx_code code = GET_CODE (operands[1]); rtx cc_reg = gen_compare_reg (code, XEXP (operands[1], 0), XEXP (operands[1], 1)); operands[1] = gen_rtx_fmt_ee (code, VOIDmode, cc_reg, const0_rtx); }") (define_insn "*movsicc_insn" [(set (match_operand:SI 0 "register_operand" "=r") (if_then_else:SI (match_operand 1 "comparison_operator" "") (match_operand:SI 2 "nonmemory_operand" "rJi") (match_operand:SI 3 "register_operand" "0")))] "" "mov.%d1 %0,%S2" [(set_attr "type" "cmove")]) (define_insn "*movsfcc_insn" [(set (match_operand:SF 0 "register_operand" "=r,r") (if_then_else:SF (match_operand 1 "comparison_operator" "") (match_operand:SF 2 "nonmemory_operand" "r,E") (match_operand:SF 3 "register_operand" "0,0")))] "" "@ mov.%d1 %0,%2 mov.%d1 %0,%2 ; %A2" [(set_attr "type" "cmove,cmove")]) ;; Zero extension instructions. ;; ??? We don't support volatile memrefs here, but I'm not sure why. (define_insn "zero_extendqihi2" [(set (match_operand:HI 0 "register_operand" "=r,r") (zero_extend:HI (match_operand:QI 1 "nonvol_nonimm_operand" "r,m")))] "" "@ extb%? %0,%1 ldb%U1 %0,%1" [(set_attr "type" "unary,load")]) (define_insn "*zero_extendqihi2_set_cc_insn" [(set (reg:CCZN 61) (compare:CCZN (zero_extend:SI (match_operand:QI 1 "register_operand" "r")) (const_int 0))) (set (match_operand:HI 0 "register_operand" "=r") (zero_extend:HI (match_dup 1)))] "" "extb%?.f %0,%1" [(set_attr "type" "unary") (set_attr "cond" "set_zn")]) (define_insn "zero_extendqisi2" [(set (match_operand:SI 0 "register_operand" "=r,r") (zero_extend:SI (match_operand:QI 1 "nonvol_nonimm_operand" "r,m")))] "" "@ extb%? %0,%1 ldb%U1 %0,%1" [(set_attr "type" "unary,load")]) (define_insn "*zero_extendqisi2_set_cc_insn" [(set (reg:CCZN 61) (compare:CCZN (zero_extend:SI (match_operand:QI 1 "register_operand" "r")) (const_int 0))) (set (match_operand:SI 0 "register_operand" "=r") (zero_extend:SI (match_dup 1)))] "" "extb%?.f %0,%1" [(set_attr "type" "unary") (set_attr "cond" "set_zn")]) (define_insn "zero_extendhisi2" [(set (match_operand:SI 0 "register_operand" "=r,r") (zero_extend:SI (match_operand:HI 1 "nonvol_nonimm_operand" "r,m")))] "" "@ extw%? %0,%1 ldw%U1 %0,%1" [(set_attr "type" "unary,load")]) (define_insn "*zero_extendhisi2_set_cc_insn" [(set (reg:CCZN 61) (compare:CCZN (zero_extend:SI (match_operand:HI 1 "register_operand" "r")) (const_int 0))) (set (match_operand:SI 0 "register_operand" "=r") (zero_extend:SI (match_dup 1)))] "" "extw%?.f %0,%1" [(set_attr "type" "unary") (set_attr "cond" "set_zn")]) ;; Sign extension instructions. (define_insn "extendqihi2" [(set (match_operand:HI 0 "register_operand" "=r,r") (sign_extend:HI (match_operand:QI 1 "nonvol_nonimm_operand" "r,m")))] "" "@ sexb%? %0,%1 ldb.x%U1 %0,%1" [(set_attr "type" "unary,load")]) (define_insn "*extendqihi2_set_cc_insn" [(set (reg:CCZN 61) (compare:CCZN (sign_extend:SI (match_operand:QI 1 "register_operand" "r")) (const_int 0))) (set (match_operand:HI 0 "register_operand" "=r") (sign_extend:HI (match_dup 1)))] "" "sexb%?.f %0,%1" [(set_attr "type" "unary") (set_attr "cond" "set_zn")]) (define_insn "extendqisi2" [(set (match_operand:SI 0 "register_operand" "=r,r") (sign_extend:SI (match_operand:QI 1 "nonvol_nonimm_operand" "r,m")))] "" "@ sexb%? %0,%1 ldb.x%U1 %0,%1" [(set_attr "type" "unary,load")]) (define_insn "*extendqisi2_set_cc_insn" [(set (reg:CCZN 61) (compare:CCZN (sign_extend:SI (match_operand:QI 1 "register_operand" "r")) (const_int 0))) (set (match_operand:SI 0 "register_operand" "=r") (sign_extend:SI (match_dup 1)))] "" "sexb%?.f %0,%1" [(set_attr "type" "unary") (set_attr "cond" "set_zn")]) (define_insn "extendhisi2" [(set (match_operand:SI 0 "register_operand" "=r,r") (sign_extend:SI (match_operand:HI 1 "nonvol_nonimm_operand" "r,m")))] "" "@ sexw%? %0,%1 ldw.x%U1 %0,%1" [(set_attr "type" "unary,load")]) (define_insn "*extendhisi2_set_cc_insn" [(set (reg:CCZN 61) (compare:CCZN (sign_extend:SI (match_operand:HI 1 "register_operand" "r")) (const_int 0))) (set (match_operand:SI 0 "register_operand" "=r") (sign_extend:SI (match_dup 1)))] "" "sexw%?.f %0,%1" [(set_attr "type" "unary") (set_attr "cond" "set_zn")]) ;; Arithmetic instructions. (define_insn "addsi3" [(set (match_operand:SI 0 "register_operand" "=r") (plus:SI (match_operand:SI 1 "register_operand" "%r") (match_operand:SI 2 "nonmemory_operand" "rIJ")))] "" "add%? %0,%1,%2") (define_insn "*addsi3_set_cc_insn" [(set (reg:CC 61) (compare:CC (plus:SI (match_operand:SI 1 "register_operand" "%r") (match_operand:SI 2 "nonmemory_operand" "rIJ")) (const_int 0))) (set (match_operand:SI 0 "register_operand" "=r") (plus:SI (match_dup 1) (match_dup 2)))] "" "add%?.f %0,%1,%2" [(set_attr "cond" "set")]) (define_insn "adddi3" [(set (match_operand:DI 0 "register_operand" "=r") (plus:DI (match_operand:DI 1 "nonmemory_operand" "%r") (match_operand:DI 2 "nonmemory_operand" "ri"))) (clobber (reg:CC 61))] "" "* { rtx op2 = operands[2]; if (GET_CODE (op2) == CONST_INT) { int sign = INTVAL (op2); if (sign < 0) return \"add.f %L0,%L1,%2\;adc %H0,%H1,-1\"; else return \"add.f %L0,%L1,%2\;adc %H0,%H1,0\"; } else return \"add.f %L0,%L1,%L2\;adc %H0,%H1,%H2\"; }" [(set_attr "length" "2")]) (define_insn "subsi3" [(set (match_operand:SI 0 "register_operand" "=r") (minus:SI (match_operand:SI 1 "register_operand" "r") (match_operand:SI 2 "nonmemory_operand" "rIJ")))] "" "sub%? %0,%1,%2") (define_insn "*subsi3_set_cc_insn" [(set (reg:CC 61) (compare:CC (minus:SI (match_operand:SI 1 "register_operand" "%r") (match_operand:SI 2 "nonmemory_operand" "rIJ")) (const_int 0))) (set (match_operand:SI 0 "register_operand" "=r") (minus:SI (match_dup 1) (match_dup 2)))] "" "sub%?.f %0,%1,%2" [(set_attr "cond" "set")]) (define_insn "subdi3" [(set (match_operand:DI 0 "register_operand" "=r") (minus:DI (match_operand:DI 1 "nonmemory_operand" "r") (match_operand:DI 2 "nonmemory_operand" "ri"))) (clobber (reg:CC 61))] "" "* { rtx op2 = operands[2]; if (GET_CODE (op2) == CONST_INT) { int sign = INTVAL (op2); if (sign < 0) return \"sub.f %L0,%L1,%2\;sbc %H0,%H1,-1\"; else return \"sub.f %L0,%L1,%2\;sbc %H0,%H1,0\"; } else return \"sub.f %L0,%L1,%L2\;sbc %H0,%H1,%H2\"; }" [(set_attr "length" "2")]) ;; Boolean instructions. ;; ;; We don't define the DImode versions as expand_binop does a good enough job. (define_insn "andsi3" [(set (match_operand:SI 0 "register_operand" "=r") (and:SI (match_operand:SI 1 "register_operand" "%r") (match_operand:SI 2 "nonmemory_operand" "rIJ")))] "" "and%? %0,%1,%2") (define_insn "*andsi3_set_cc_insn" [(set (reg:CCZN 61) (compare:CCZN (and:SI (match_operand:SI 1 "register_operand" "%r") (match_operand:SI 2 "nonmemory_operand" "rIJ")) (const_int 0))) (set (match_operand:SI 0 "register_operand" "=r") (and:SI (match_dup 1) (match_dup 2)))] "" "and%?.f %0,%1,%2" [(set_attr "cond" "set_zn")]) (define_insn "*bicsi3_insn" [(set (match_operand:SI 0 "register_operand" "=r,r,r,r") (and:SI (match_operand:SI 1 "nonmemory_operand" "r,r,I,J") (not:SI (match_operand:SI 2 "nonmemory_operand" "rI,J,r,r"))))] "" "bic%? %0,%1,%2" [(set_attr "length" "1,2,1,2")]) (define_insn "*bicsi3_set_cc_insn" [(set (reg:CCZN 61) (compare:CCZN (and:SI (match_operand:SI 1 "register_operand" "%r") (not:SI (match_operand:SI 2 "nonmemory_operand" "rIJ"))) (const_int 0))) (set (match_operand:SI 0 "register_operand" "=r") (and:SI (match_dup 1) (not:SI (match_dup 2))))] "" "bic%?.f %0,%1,%2" [(set_attr "cond" "set_zn")]) (define_insn "iorsi3" [(set (match_operand:SI 0 "register_operand" "=r") (ior:SI (match_operand:SI 1 "register_operand" "%r") (match_operand:SI 2 "nonmemory_operand" "rIJ")))] "" "or%? %0,%1,%2") (define_insn "*iorsi3_set_cc_insn" [(set (reg:CCZN 61) (compare:CCZN (ior:SI (match_operand:SI 1 "register_operand" "%r") (match_operand:SI 2 "nonmemory_operand" "rIJ")) (const_int 0))) (set (match_operand:SI 0 "register_operand" "=r") (ior:SI (match_dup 1) (match_dup 2)))] "" "or%?.f %0,%1,%2" [(set_attr "cond" "set_zn")]) (define_insn "xorsi3" [(set (match_operand:SI 0 "register_operand" "=r") (xor:SI (match_operand:SI 1 "register_operand" "%r") (match_operand:SI 2 "nonmemory_operand" "rIJ")))] "" "xor%? %0,%1,%2") (define_insn "*xorsi3_set_cc_insn" [(set (reg:CCZN 61) (compare:CCZN (xor:SI (match_operand:SI 1 "register_operand" "%r") (match_operand:SI 2 "nonmemory_operand" "rIJ")) (const_int 0))) (set (match_operand:SI 0 "register_operand" "=r") (xor:SI (match_dup 1) (match_dup 2)))] "" "xor%?.f %0,%1,%2" [(set_attr "cond" "set_zn")]) (define_insn "negsi2" [(set (match_operand:SI 0 "register_operand" "=r") (neg:SI (match_operand:SI 1 "register_operand" "r")))] "" "sub%? %0,0,%1" [(set_attr "type" "unary")]) (define_insn "*negsi2_set_cc_insn" [(set (reg:CC 61) (compare:CC (neg:SI (match_operand:SI 1 "register_operand" "r")) (const_int 0))) (set (match_operand:SI 0 "register_operand" "=r") (neg:SI (match_dup 1)))] "" "sub%?.f %0,0,%1" [(set_attr "type" "unary") (set_attr "cond" "set")]) (define_insn "negdi2" [(set (match_operand:DI 0 "register_operand" "=r") (neg:DI (match_operand:DI 1 "register_operand" "r"))) (clobber (reg:SI 61))] "" "sub.f %L0,0,%L1\;sbc %H0,0,%H1" [(set_attr "type" "unary") (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")))] "" "xor%? %0,%1,-1" [(set_attr "type" "unary")]) (define_insn "*one_cmplsi2_set_cc_insn" [(set (reg:CCZN 61) (compare:CCZN (not:SI (match_operand:SI 1 "register_operand" "r")) (const_int 0))) (set (match_operand:SI 0 "register_operand" "=r") (not:SI (match_dup 1)))] "" "xor%?.f %0,%1,-1" [(set_attr "type" "unary") (set_attr "cond" "set_zn")]) ;; Shift instructions. (define_expand "ashlsi3" [(set (match_operand:SI 0 "register_operand" "") (ashift:SI (match_operand:SI 1 "register_operand" "") (match_operand:SI 2 "nonmemory_operand" "")))] "" " { if (! TARGET_SHIFTER) { emit_insn (gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, gen_rtx_SET (VOIDmode, operands[0], gen_rtx_ASHIFT (SImode, operands[1], operands[2])), gen_rtx_CLOBBER (VOIDmode, gen_rtx_SCRATCH (SImode))))); DONE; } }") (define_expand "ashrsi3" [(set (match_operand:SI 0 "register_operand" "") (ashiftrt:SI (match_operand:SI 1 "register_operand" "") (match_operand:SI 2 "nonmemory_operand" "")))] "" " { if (! TARGET_SHIFTER) { emit_insn (gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, gen_rtx_SET (VOIDmode, operands[0], gen_rtx_ASHIFTRT (SImode, operands[1], operands[2])), gen_rtx_CLOBBER (VOIDmode, gen_rtx_SCRATCH (SImode))))); DONE; } }") (define_expand "lshrsi3" [(set (match_operand:SI 0 "register_operand" "") (lshiftrt:SI (match_operand:SI 1 "register_operand" "") (match_operand:SI 2 "nonmemory_operand" "")))] "" " { if (! TARGET_SHIFTER) { emit_insn (gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, gen_rtx_SET (VOIDmode, operands[0], gen_rtx_LSHIFTRT (SImode, operands[1], operands[2])), gen_rtx_CLOBBER (VOIDmode, gen_rtx_SCRATCH (SImode))))); DONE; } }") (define_insn "*ashlsi3_insn" [(set (match_operand:SI 0 "register_operand" "=r,r,r,r") (ashift:SI (match_operand:SI 1 "nonmemory_operand" "r,r,I,J") (match_operand:SI 2 "nonmemory_operand" "rI,J,r,r")))] "TARGET_SHIFTER" "asl%? %0,%1,%2" [(set_attr "type" "shift") (set_attr "length" "1,2,1,2")]) (define_insn "*ashrsi3_insn" [(set (match_operand:SI 0 "register_operand" "=r,r,r,r") (ashiftrt:SI (match_operand:SI 1 "nonmemory_operand" "r,r,I,J") (match_operand:SI 2 "nonmemory_operand" "rI,J,r,r")))] "TARGET_SHIFTER" "asr%? %0,%1,%2" [(set_attr "type" "shift") (set_attr "length" "1,2,1,2")]) (define_insn "*lshrsi3_insn" [(set (match_operand:SI 0 "register_operand" "=r,r,r,r") (lshiftrt:SI (match_operand:SI 1 "nonmemory_operand" "r,r,I,J") (match_operand:SI 2 "nonmemory_operand" "rI,J,r,r")))] "TARGET_SHIFTER" "lsr%? %0,%1,%2" [(set_attr "type" "shift") (set_attr "length" "1,2,1,2")]) (define_insn "*shift_si3" [(set (match_operand:SI 0 "register_operand" "=r") (match_operator:SI 3 "shift_operator" [(match_operand:SI 1 "register_operand" "0") (match_operand:SI 2 "nonmemory_operand" "rIJ")])) (clobber (match_scratch:SI 4 "=&r"))] "! TARGET_SHIFTER" "* return output_shift (operands);" [(set_attr "type" "shift") (set_attr "length" "8")]) ;; Compare instructions. ;; This controls RTL generation and register allocation. ;; ??? We may be able to relax this a bit by adding a new constant 'K' for 0. ;; This assumes sub.f 0,symbol,0 is a valid insn. ;; Note that "sub.f 0,r0,1" is an 8 byte insn. To avoid unnecessarily ;; creating 8 byte insns we duplicate %1 in the destination reg of the insn ;; if it's a small constant. (define_insn "*cmpsi_cc_insn" [(set (reg:CC 61) (compare:CC (match_operand:SI 0 "register_operand" "r,r,r") (match_operand:SI 1 "nonmemory_operand" "r,I,J")))] "" "@ sub.f 0,%0,%1 sub.f %1,%0,%1 sub.f 0,%0,%1" [(set_attr "type" "compare,compare,compare")]) (define_insn "*cmpsi_cczn_insn" [(set (reg:CCZN 61) (compare:CCZN (match_operand:SI 0 "register_operand" "r,r,r") (match_operand:SI 1 "nonmemory_operand" "r,I,J")))] "" "@ sub.f 0,%0,%1 sub.f %1,%0,%1 sub.f 0,%0,%1" [(set_attr "type" "compare,compare,compare")]) (define_insn "*cmpsi_ccznc_insn" [(set (reg:CCZNC 61) (compare:CCZNC (match_operand:SI 0 "register_operand" "r,r,r") (match_operand:SI 1 "nonmemory_operand" "r,I,J")))] "" "@ sub.f 0,%0,%1 sub.f %1,%0,%1 sub.f 0,%0,%1" [(set_attr "type" "compare,compare,compare")]) ;; Next come the scc insn and its expander. (define_expand "cstoresi4" [(set (match_dup 4) (match_op_dup 5 [(match_operand:SI 2 "register_operand" "") (match_operand:SI 3 "nonmemory_operand" "")])) (set (match_operand:SI 0 "register_operand") (match_operator:SI 1 "ordered_comparison_operator" [(match_dup 4) (const_int 0)]))] "" " { operands[4] = gen_compare_reg (GET_CODE (operands[1]), operands[2], operands[3]); operands[5] = gen_rtx_fmt_ee (COMPARE, GET_MODE (operands[4]), operands[2], operands[3]); }") (define_insn "*scc_insn" [(set (match_operand:SI 0 "register_operand" "=r") (match_operator:SI 1 "comparison_operator" [(reg 61) (const_int 0)]))] "" "mov %0,1\;sub.%D1 %0,%0,%0" [(set_attr "type" "unary") (set_attr "length" "2")]) ;; ??? Look up negscc insn. See pa.md for example. (define_insn "*neg_scc_insn" [(set (match_operand:SI 0 "register_operand" "=r") (neg:SI (match_operator:SI 1 "comparison_operator" [(reg 61) (const_int 0)])))] "" "mov %0,-1\;sub.%D1 %0,%0,%0" [(set_attr "type" "unary") (set_attr "length" "2")]) (define_insn "*not_scc_insn" [(set (match_operand:SI 0 "register_operand" "=r") (not:SI (match_operator:SI 1 "comparison_operator" [(reg 61) (const_int 0)])))] "" "mov %0,1\;sub.%d1 %0,%0,%0" [(set_attr "type" "unary") (set_attr "length" "2")]) ;; These control RTL generation for conditional jump insns (define_expand "cbranchsi4" [(set (match_dup 4) (match_op_dup 5 [(match_operand:SI 1 "register_operand" "") (match_operand:SI 2 "nonmemory_operand" "")])) (set (pc) (if_then_else (match_operator 0 "ordered_comparison_operator" [(match_dup 4) (const_int 0)]) (label_ref (match_operand 3 "" "")) (pc)))] "" " { operands[4] = gen_compare_reg (GET_CODE (operands[0]), operands[1], operands[2]); operands[5] = gen_rtx_fmt_ee (COMPARE, GET_MODE (operands[4]), operands[1], operands[2]); }") ;; Now match both normal and inverted jump. (define_insn "*branch_insn" [(set (pc) (if_then_else (match_operator 1 "proper_comparison_operator" [(reg 61) (const_int 0)]) (label_ref (match_operand 0 "" "")) (pc)))] "" "* { if (arc_ccfsm_branch_deleted_p ()) { arc_ccfsm_record_branch_deleted (); return \"; branch deleted, next insns conditionalized\"; } else return \"%~b%d1%# %l0\"; }" [(set_attr "type" "branch")]) (define_insn "*rev_branch_insn" [(set (pc) (if_then_else (match_operator 1 "proper_comparison_operator" [(reg 61) (const_int 0)]) (pc) (label_ref (match_operand 0 "" ""))))] "REVERSIBLE_CC_MODE (GET_MODE (XEXP (operands[1], 0)))" "* { if (arc_ccfsm_branch_deleted_p ()) { arc_ccfsm_record_branch_deleted (); return \"; branch deleted, next insns conditionalized\"; } else return \"%~b%D1%# %l0\"; }" [(set_attr "type" "branch")]) ;; Unconditional and other jump instructions. (define_insn "jump" [(set (pc) (label_ref (match_operand 0 "" "")))] "" "b%* %l0" [(set_attr "type" "uncond_branch")]) (define_insn "indirect_jump" [(set (pc) (match_operand:SI 0 "address_operand" "p"))] "" "j%* %a0" [(set_attr "type" "uncond_branch")]) ;; Implement a switch statement. ;; This wouldn't be necessary in the non-pic case if we could distinguish ;; label refs of the jump table from other label refs. The problem is that ;; label refs are output as "%st(.LL42)" but we don't want the %st - we want ;; the real address since it's the address of the table. (define_expand "casesi" [(set (match_dup 5) (minus:SI (match_operand:SI 0 "register_operand" "") (match_operand:SI 1 "nonmemory_operand" ""))) (set (reg:CC 61) (compare:CC (match_dup 5) (match_operand:SI 2 "nonmemory_operand" ""))) (set (pc) (if_then_else (gtu (reg:CC 61) (const_int 0)) (label_ref (match_operand 4 "" "")) (pc))) (parallel [(set (pc) (mem:SI (plus:SI (mult:SI (match_dup 5) (const_int 4)) (label_ref (match_operand 3 "" ""))))) (clobber (match_scratch:SI 6 "")) (clobber (match_scratch:SI 7 ""))])] "" " { operands[5] = gen_reg_rtx (SImode); }") (define_insn "*casesi_insn" [(set (pc) (mem:SI (plus:SI (mult:SI (match_operand:SI 0 "register_operand" "r") (const_int 4)) (label_ref (match_operand 1 "" ""))))) (clobber (match_scratch:SI 2 "=r")) (clobber (match_scratch:SI 3 "=r"))] "" "* { output_asm_insn (\"mov %2,%1\", operands); if (TARGET_SHIFTER) output_asm_insn (\"asl %3,%0,2\", operands); else output_asm_insn (\"asl %3,%0\;asl %3,%3\", operands); output_asm_insn (\"ld %2,[%2,%3]\", operands); output_asm_insn (\"j.nd %a2\", operands); return \"\"; }" [(set_attr "type" "uncond_branch") (set_attr "length" "6")]) (define_insn "tablejump" [(set (pc) (match_operand:SI 0 "address_operand" "p")) (use (label_ref (match_operand 1 "" "")))] "0 /* disabled -> using casesi now */" "j%* %a0" [(set_attr "type" "uncond_branch")]) (define_expand "call" ;; operands[1] is stack_size_rtx ;; operands[2] is next_arg_register [(parallel [(call (match_operand:SI 0 "call_operand" "") (match_operand 1 "" "")) (clobber (reg:SI 31))])] "" "") (define_insn "*call_via_reg" [(call (mem:SI (match_operand:SI 0 "register_operand" "r")) (match_operand 1 "" "")) (clobber (reg:SI 31))] "" "lr blink,[status]\;j.d %0\;add blink,blink,2" [(set_attr "type" "call_no_delay_slot") (set_attr "length" "3")]) (define_insn "*call_via_label" [(call (mem:SI (match_operand:SI 0 "call_address_operand" "")) (match_operand 1 "" "")) (clobber (reg:SI 31))] "" ; The %~ is necessary in case this insn gets conditionalized and the previous ; insn is the cc setter. "%~bl%!%* %0" [(set_attr "type" "call") (set_attr "cond" "canuse")]) (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 31))])] "" "") (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 31))] "" "lr blink,[status]\;j.d %1\;add blink,blink,2" [(set_attr "type" "call_no_delay_slot") (set_attr "length" "3")]) (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 31))] "" ; The %~ is necessary in case this insn gets conditionalized and the previous ; insn is the cc setter. "%~bl%!%* %1" [(set_attr "type" "call") (set_attr "cond" "canuse")]) (define_insn "nop" [(const_int 0)] "" "nop" [(set_attr "type" "misc")]) ;; Special pattern to flush the icache. ;; ??? Not sure what to do here. Some ARC's are known to support this. (define_insn "flush_icache" [(unspec_volatile [(match_operand 0 "memory_operand" "m")] 0)] "" "* return \"\";" [(set_attr "type" "misc")]) ;; Split up troublesome insns for better scheduling. ;; Peepholes go at the end.