;; Machine description of Andes NDS32 cpu for GNU compiler ;; Copyright (C) 2012-2017 Free Software Foundation, Inc. ;; Contributed by Andes Technology Corporation. ;; ;; 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. ;; Include predicates definition. (include "predicates.md") ;; Include constraints definition. (include "constraints.md") ;; Include iterators definition. (include "iterators.md") ;; Include pipelines definition. (include "pipelines.md") ;; Include constants definition. (include "constants.md") ;; Include intrinsic functions definition. (include "nds32-intrinsic.md") ;; Include block move for nds32 multiple load/store behavior. (include "nds32-multiple.md") ;; Include DImode/DFmode operations. (include "nds32-doubleword.md") ;; Include peephole patterns. (include "nds32-peephole2.md") ;; Insn type, it is used to default other attribute values. (define_attr "type" "unknown,move,load,store,alu,compare,branch,call,misc" (const_string "unknown")) ;; Length, in bytes, default is 4-bytes. (define_attr "length" "" (const_int 4)) ;; Enabled, which is used to enable/disable insn alternatives. ;; Note that we use length and TARGET_16_BIT here as criteria. ;; If the instruction pattern already check TARGET_16_BIT to ;; determine the length by itself, its enabled attribute should be ;; always 1 to avoid the conflict with the settings here. (define_attr "enabled" "" (cond [(and (eq_attr "length" "2") (match_test "!TARGET_16_BIT")) (const_int 0)] (const_int 1))) ;; ---------------------------------------------------------------------------- ;; Move instructions. ;; For QImode and HImode, the immediate value can be fit in imm20s. ;; So there is no need to split rtx for QI and HI patterns. (define_expand "movqi" [(set (match_operand:QI 0 "general_operand" "") (match_operand:QI 1 "general_operand" ""))] "" { /* Need to force register if mem <- !reg. */ if (MEM_P (operands[0]) && !REG_P (operands[1])) operands[1] = force_reg (QImode, operands[1]); }) (define_expand "movhi" [(set (match_operand:HI 0 "general_operand" "") (match_operand:HI 1 "general_operand" ""))] "" { /* Need to force register if mem <- !reg. */ if (MEM_P (operands[0]) && !REG_P (operands[1])) operands[1] = force_reg (HImode, operands[1]); }) (define_expand "movsi" [(set (match_operand:SI 0 "general_operand" "") (match_operand:SI 1 "general_operand" ""))] "" { /* Need to force register if mem <- !reg. */ if (MEM_P (operands[0]) && !REG_P (operands[1])) operands[1] = force_reg (SImode, operands[1]); /* If operands[1] is a large constant and cannot be performed by a single instruction, we need to split it. */ if (CONST_INT_P (operands[1]) && !satisfies_constraint_Is20 (operands[1]) && !satisfies_constraint_Ihig (operands[1])) { rtx high20_rtx; HOST_WIDE_INT low12_int; rtx tmp_rtx; tmp_rtx = can_create_pseudo_p () ? gen_reg_rtx (SImode) : operands[0]; high20_rtx = gen_int_mode ((INTVAL (operands[1]) >> 12) << 12, SImode); low12_int = INTVAL (operands[1]) & 0xfff; emit_move_insn (tmp_rtx, high20_rtx); emit_move_insn (operands[0], plus_constant (SImode, tmp_rtx, low12_int)); DONE; } }) (define_insn "*mov" [(set (match_operand:QIHISI 0 "nonimmediate_operand" "=r, r, U45, U33, U37, U45, m, l, l, l, d, r, d, r, r, r") (match_operand:QIHISI 1 "nds32_move_operand" " r, r, l, l, l, d, r, U45, U33, U37, U45, m, Ip05, Is05, Is20, Ihig"))] "" { switch (which_alternative) { case 0: return "mov55\t%0, %1"; case 1: return "ori\t%0, %1, 0"; case 2: case 3: case 4: case 5: return nds32_output_16bit_store (operands, ); case 6: return nds32_output_32bit_store (operands, ); case 7: case 8: case 9: case 10: return nds32_output_16bit_load (operands, ); case 11: return nds32_output_32bit_load (operands, ); case 12: return "movpi45\t%0, %1"; case 13: return "movi55\t%0, %1"; case 14: return "movi\t%0, %1"; case 15: return "sethi\t%0, hi20(%1)"; default: gcc_unreachable (); } } [(set_attr "type" "alu,alu,store,store,store,store,store,load,load,load,load,load,alu,alu,alu,alu") (set_attr "length" " 2, 4, 2, 2, 2, 2, 4, 2, 2, 2, 2, 4, 2, 2, 4, 4")]) ;; We use nds32_symbolic_operand to limit that only CONST/SYMBOL_REF/LABEL_REF ;; are able to match such instruction template. (define_insn "*move_addr" [(set (match_operand:SI 0 "register_operand" "=l, r") (match_operand:SI 1 "nds32_symbolic_operand" " i, i"))] "" "la\t%0, %1" [(set_attr "type" "move") (set_attr "length" "8")]) (define_insn "*sethi" [(set (match_operand:SI 0 "register_operand" "=r") (high:SI (match_operand:SI 1 "nds32_symbolic_operand" " i")))] "" "sethi\t%0, hi20(%1)" [(set_attr "type" "alu") (set_attr "length" "4")]) (define_insn "*lo_sum" [(set (match_operand:SI 0 "register_operand" "=r") (lo_sum:SI (match_operand:SI 1 "register_operand" " r") (match_operand:SI 2 "nds32_symbolic_operand" " i")))] "" "ori\t%0, %1, lo12(%2)" [(set_attr "type" "alu") (set_attr "length" "4")]) ;; ---------------------------------------------------------------------------- ;; Zero extension instructions. (define_insn "zero_extendsi2" [(set (match_operand:SI 0 "register_operand" "=l, r, l, *r") (zero_extend:SI (match_operand:QIHI 1 "nonimmediate_operand" " l, r, U33, m")))] "" { switch (which_alternative) { case 0: return "ze33\t%0, %1"; case 1: return "ze\t%0, %1"; case 2: return nds32_output_16bit_load (operands, ); case 3: return nds32_output_32bit_load (operands, ); default: gcc_unreachable (); } } [(set_attr "type" "alu,alu,load,load") (set_attr "length" " 2, 4, 2, 4")]) ;; Sign extension instructions. (define_insn "extendsi2" [(set (match_operand:SI 0 "register_operand" "=l, r, r") (sign_extend:SI (match_operand:QIHI 1 "nonimmediate_operand" " l, r, m")))] "" { switch (which_alternative) { case 0: return "se33\t%0, %1"; case 1: return "se\t%0, %1"; case 2: return nds32_output_32bit_load_s (operands, ); default: gcc_unreachable (); } } [(set_attr "type" "alu,alu,load") (set_attr "length" " 2, 4, 4")]) ;; ---------------------------------------------------------------------------- ;; Arithmetic instructions. (define_insn "add3" [(set (match_operand:QIHISI 0 "register_operand" "= d, l, d, l, d, l, k, l, r, r") (plus:QIHISI (match_operand:QIHISI 1 "register_operand" "% 0, l, 0, l, 0, l, 0, k, r, r") (match_operand:QIHISI 2 "nds32_rimm15s_operand" " In05, In03, Iu05, Iu03, r, l, Is10, Iu06, Is15, r")))] "" { switch (which_alternative) { case 0: /* addi Rt4,Rt4,-x ==> subi45 Rt4,x where 0 <= x <= 31 */ operands[2] = gen_int_mode (-INTVAL (operands[2]), SImode); return "subi45\t%0, %2"; case 1: /* addi Rt3,Ra3,-x ==> subi333 Rt3,Ra3,x where 0 <= x <= 7 */ operands[2] = gen_int_mode (-INTVAL (operands[2]), SImode); return "subi333\t%0, %1, %2"; case 2: return "addi45\t%0, %2"; case 3: return "addi333\t%0, %1, %2"; case 4: return "add45\t%0, %2"; case 5: return "add333\t%0, %1, %2"; case 6: return "addi10.sp\t%2"; case 7: return "addri36.sp\t%0, %2"; case 8: return "addi\t%0, %1, %2"; case 9: return "add\t%0, %1, %2"; default: gcc_unreachable (); } } [(set_attr "type" "alu,alu,alu,alu,alu,alu,alu,alu,alu,alu") (set_attr "length" " 2, 2, 2, 2, 2, 2, 2, 2, 4, 4")]) (define_insn "sub3" [(set (match_operand:QIHISI 0 "register_operand" "=d, l, r, r") (minus:QIHISI (match_operand:QIHISI 1 "nds32_rimm15s_operand" " 0, l, Is15, r") (match_operand:QIHISI 2 "register_operand" " r, l, r, r")))] "" "@ sub45\t%0, %2 sub333\t%0, %1, %2 subri\t%0, %2, %1 sub\t%0, %1, %2" [(set_attr "type" "alu,alu,alu,alu") (set_attr "length" " 2, 2, 4, 4")]) ;; GCC intends to simplify (plus (ashift ...) (reg)) ;; into (plus (mult ...) (reg)), so our matching pattern takes 'mult' ;; and needs to ensure it is exact_log2 value. (define_insn "*add_slli" [(set (match_operand:SI 0 "register_operand" "=r") (plus:SI (mult:SI (match_operand:SI 1 "register_operand" " r") (match_operand:SI 2 "immediate_operand" " i")) (match_operand:SI 3 "register_operand" " r")))] "TARGET_ISA_V3 && (exact_log2 (INTVAL (operands[2])) != -1) && (exact_log2 (INTVAL (operands[2])) <= 31)" { /* Get floor_log2 of the immediate value so that we can generate 'add_slli' instruction. */ operands[2] = GEN_INT (floor_log2 (INTVAL (operands[2]))); return "add_slli\t%0, %3, %1, %2"; } [(set_attr "type" "alu") (set_attr "length" "4")]) (define_insn "*add_srli" [(set (match_operand:SI 0 "register_operand" "= r") (plus:SI (lshiftrt:SI (match_operand:SI 1 "register_operand" " r") (match_operand:SI 2 "immediate_operand" " Iu05")) (match_operand:SI 3 "register_operand" " r")))] "TARGET_ISA_V3" "add_srli\t%0, %3, %1, %2" [(set_attr "type" "alu") (set_attr "length" "4")]) ;; GCC intends to simplify (minus (reg) (ashift ...)) ;; into (minus (reg) (mult ...)), so our matching pattern takes 'mult' ;; and needs to ensure it is exact_log2 value. (define_insn "*sub_slli" [(set (match_operand:SI 0 "register_operand" "=r") (minus:SI (match_operand:SI 1 "register_operand" " r") (mult:SI (match_operand:SI 2 "register_operand" " r") (match_operand:SI 3 "immediate_operand" " i"))))] "TARGET_ISA_V3 && (exact_log2 (INTVAL (operands[3])) != -1) && (exact_log2 (INTVAL (operands[3])) <= 31)" { /* Get floor_log2 of the immediate value so that we can generate 'sub_slli' instruction. */ operands[3] = GEN_INT (floor_log2 (INTVAL (operands[3]))); return "sub_slli\t%0, %1, %2, %3"; } [(set_attr "type" "alu") (set_attr "length" "4")]) (define_insn "*sub_srli" [(set (match_operand:SI 0 "register_operand" "= r") (minus:SI (match_operand:SI 1 "register_operand" " r") (lshiftrt:SI (match_operand:SI 2 "register_operand" " r") (match_operand:SI 3 "immediate_operand" " Iu05"))))] "TARGET_ISA_V3" "sub_srli\t%0, %1, %2, %3" [(set_attr "type" "alu") (set_attr "length" "4")]) ;; Multiplication instructions. (define_insn "mulsi3" [(set (match_operand:SI 0 "register_operand" "=w, r") (mult:SI (match_operand:SI 1 "register_operand" "%0, r") (match_operand:SI 2 "register_operand" " w, r")))] "" "@ mul33\t%0, %2 mul\t%0, %1, %2" [(set_attr "type" "alu,alu") (set_attr "length" " 2, 4")]) (define_insn "mulsidi3" [(set (match_operand:DI 0 "register_operand" "=r") (mult:DI (sign_extend:DI (match_operand:SI 1 "register_operand" " r")) (sign_extend:DI (match_operand:SI 2 "register_operand" " r"))))] "TARGET_ISA_V2 || TARGET_ISA_V3" "mulsr64\t%0, %1, %2" [(set_attr "type" "alu") (set_attr "length" "4")]) (define_insn "umulsidi3" [(set (match_operand:DI 0 "register_operand" "=r") (mult:DI (zero_extend:DI (match_operand:SI 1 "register_operand" " r")) (zero_extend:DI (match_operand:SI 2 "register_operand" " r"))))] "TARGET_ISA_V2 || TARGET_ISA_V3" "mulr64\t%0, %1, %2" [(set_attr "type" "alu") (set_attr "length" "4")]) ;; Multiply-accumulate instructions. (define_insn "*maddr32_0" [(set (match_operand:SI 0 "register_operand" "=r") (plus:SI (match_operand:SI 3 "register_operand" " 0") (mult:SI (match_operand:SI 1 "register_operand" " r") (match_operand:SI 2 "register_operand" " r"))))] "" "maddr32\t%0, %1, %2" [(set_attr "type" "alu") (set_attr "length" "4")]) (define_insn "*maddr32_1" [(set (match_operand:SI 0 "register_operand" "=r") (plus:SI (mult:SI (match_operand:SI 1 "register_operand" " r") (match_operand:SI 2 "register_operand" " r")) (match_operand:SI 3 "register_operand" " 0")))] "" "maddr32\t%0, %1, %2" [(set_attr "type" "alu") (set_attr "length" "4")]) (define_insn "*msubr32" [(set (match_operand:SI 0 "register_operand" "=r") (minus:SI (match_operand:SI 3 "register_operand" " 0") (mult:SI (match_operand:SI 1 "register_operand" " r") (match_operand:SI 2 "register_operand" " r"))))] "" "msubr32\t%0, %1, %2" [(set_attr "type" "alu") (set_attr "length" "4")]) ;; Div Instructions. (define_insn "divmodsi4" [(set (match_operand:SI 0 "register_operand" "=r") (div:SI (match_operand:SI 1 "register_operand" " r") (match_operand:SI 2 "register_operand" " r"))) (set (match_operand:SI 3 "register_operand" "=r") (mod:SI (match_dup 1) (match_dup 2)))] "" "divsr\t%0, %3, %1, %2" [(set_attr "type" "alu") (set_attr "length" "4")]) (define_insn "udivmodsi4" [(set (match_operand:SI 0 "register_operand" "=r") (udiv:SI (match_operand:SI 1 "register_operand" " r") (match_operand:SI 2 "register_operand" " r"))) (set (match_operand:SI 3 "register_operand" "=r") (umod:SI (match_dup 1) (match_dup 2)))] "" "divr\t%0, %3, %1, %2" [(set_attr "type" "alu") (set_attr "length" "4")]) ;; ---------------------------------------------------------------------------- ;; Boolean instructions. ;; Note: We define the DImode versions in nds32-doubleword.md. ;; ---------------------------------------------------------------------------- ;; 'AND' operation ;; ---------------------------------------------------------------------------- (define_insn "bitc" [(set (match_operand:SI 0 "register_operand" "=r") (and:SI (not:SI (match_operand:SI 1 "register_operand" " r")) (match_operand:SI 2 "register_operand" " r")))] "TARGET_ISA_V3" "bitc\t%0, %2, %1" [(set_attr "type" "alu") (set_attr "length" "4")] ) (define_insn "andsi3" [(set (match_operand:SI 0 "register_operand" "=w, r, l, l, l, l, l, l, r, r, r, r, r") (and:SI (match_operand:SI 1 "register_operand" "%0, r, l, l, l, l, 0, 0, r, r, r, r, r") (match_operand:SI 2 "general_operand" " w, r, Izeb, Izeh, Ixls, Ix11, Ibms, Ifex, Izeb, Izeh, Iu15, Ii15, Ic15")))] "" { HOST_WIDE_INT mask = INTVAL (operands[2]); int zero_position; /* 16-bit andi instructions: andi Rt3,Ra3,0xff -> zeb33 Rt3,Ra3 andi Rt3,Ra3,0xffff -> zeh33 Rt3,Ra3 andi Rt3,Ra3,0x01 -> xlsb33 Rt3,Ra3 andi Rt3,Ra3,0x7ff -> x11b33 Rt3,Ra3 andi Rt3,Rt3,2^imm3u -> bmski33 Rt3,imm3u andi Rt3,Rt3,(2^(imm3u+1))-1 -> fexti33 Rt3,imm3u. */ switch (which_alternative) { case 0: return "and33\t%0, %2"; case 1: return "and\t%0, %1, %2"; case 2: return "zeb33\t%0, %1"; case 3: return "zeh33\t%0, %1"; case 4: return "xlsb33\t%0, %1"; case 5: return "x11b33\t%0, %1"; case 6: operands[2] = GEN_INT (floor_log2 (mask)); return "bmski33\t%0, %2"; case 7: operands[2] = GEN_INT (floor_log2 (mask + 1) - 1); return "fexti33\t%0, %2"; case 8: return "zeb\t%0, %1"; case 9: return "zeh\t%0, %1"; case 10: return "andi\t%0, %1, %2"; case 11: operands[2] = GEN_INT (~mask); return "bitci\t%0, %1, %2"; case 12: /* If we reach this alternative, it must pass the nds32_can_use_bclr_p() test, so that we can guarantee there is only one 0-bit within the immediate value. */ for (zero_position = 31; zero_position >= 0; zero_position--) { if ((INTVAL (operands[2]) & (1 << zero_position)) == 0) { /* Found the 0-bit position. */ operands[2] = GEN_INT (zero_position); break; } } return "bclr\t%0, %1, %2"; default: gcc_unreachable (); } } [(set_attr "type" "alu,alu,alu,alu,alu,alu,alu,alu,alu,alu,alu,alu,alu") (set_attr "length" " 2, 4, 2, 2, 2, 2, 2, 2, 4, 4, 4, 4, 4")]) (define_insn "*and_slli" [(set (match_operand:SI 0 "register_operand" "= r") (and:SI (ashift:SI (match_operand:SI 1 "register_operand" " r") (match_operand:SI 2 "immediate_operand" " Iu05")) (match_operand:SI 3 "register_operand" " r")))] "TARGET_ISA_V3" "and_slli\t%0, %3, %1, %2" [(set_attr "type" "alu") (set_attr "length" "4")]) (define_insn "*and_srli" [(set (match_operand:SI 0 "register_operand" "= r") (and:SI (lshiftrt:SI (match_operand:SI 1 "register_operand" " r") (match_operand:SI 2 "immediate_operand" " Iu05")) (match_operand:SI 3 "register_operand" " r")))] "TARGET_ISA_V3" "and_srli\t%0, %3, %1, %2" [(set_attr "type" "alu") (set_attr "length" "4")]) ;; ---------------------------------------------------------------------------- ;; 'OR' operation ;; ---------------------------------------------------------------------------- ;; For V3/V3M ISA, we have 'or33' instruction. ;; So we can identify 'or Rt3,Rt3,Ra3' case and set its length to be 2. (define_insn "iorsi3" [(set (match_operand:SI 0 "register_operand" "=w, r, r, r") (ior:SI (match_operand:SI 1 "register_operand" "%0, r, r, r") (match_operand:SI 2 "general_operand" " w, r, Iu15, Ie15")))] "" { int one_position; switch (which_alternative) { case 0: return "or33\t%0, %2"; case 1: return "or\t%0, %1, %2"; case 2: return "ori\t%0, %1, %2"; case 3: /* If we reach this alternative, it must pass the nds32_can_use_bset_p() test, so that we can guarantee there is only one 1-bit within the immediate value. */ /* Use exact_log2() to search the 1-bit position. */ one_position = exact_log2 (INTVAL (operands[2])); operands[2] = GEN_INT (one_position); return "bset\t%0, %1, %2"; default: gcc_unreachable (); } } [(set_attr "type" "alu,alu,alu,alu") (set_attr "length" " 2, 4, 4, 4")]) (define_insn "*or_slli" [(set (match_operand:SI 0 "register_operand" "= r") (ior:SI (ashift:SI (match_operand:SI 1 "register_operand" " r") (match_operand:SI 2 "immediate_operand" " Iu05")) (match_operand:SI 3 "register_operand" " r")))] "TARGET_ISA_V3" "or_slli\t%0, %3, %1, %2" [(set_attr "type" "alu") (set_attr "length" "4")]) (define_insn "*or_srli" [(set (match_operand:SI 0 "register_operand" "= r") (ior:SI (lshiftrt:SI (match_operand:SI 1 "register_operand" " r") (match_operand:SI 2 "immediate_operand" " Iu05")) (match_operand:SI 3 "register_operand" " r")))] "TARGET_ISA_V3" "or_srli\t%0, %3, %1, %2" [(set_attr "type" "alu") (set_attr "length" "4")]) ;; ---------------------------------------------------------------------------- ;; 'XOR' operation ;; ---------------------------------------------------------------------------- ;; For V3/V3M ISA, we have 'xor33' instruction. ;; So we can identify 'xor Rt3,Rt3,Ra3' case and set its length to be 2. (define_insn "xorsi3" [(set (match_operand:SI 0 "register_operand" "=w, r, r, r") (xor:SI (match_operand:SI 1 "register_operand" "%0, r, r, r") (match_operand:SI 2 "general_operand" " w, r, Iu15, It15")))] "" { int one_position; switch (which_alternative) { case 0: return "xor33\t%0, %2"; case 1: return "xor\t%0, %1, %2"; case 2: return "xori\t%0, %1, %2"; case 3: /* If we reach this alternative, it must pass the nds32_can_use_btgl_p() test, so that we can guarantee there is only one 1-bit within the immediate value. */ /* Use exact_log2() to search the 1-bit position. */ one_position = exact_log2 (INTVAL (operands[2])); operands[2] = GEN_INT (one_position); return "btgl\t%0, %1, %2"; default: gcc_unreachable (); } } [(set_attr "type" "alu,alu,alu,alu") (set_attr "length" " 2, 4, 4, 4")]) (define_insn "*xor_slli" [(set (match_operand:SI 0 "register_operand" "= r") (xor:SI (ashift:SI (match_operand:SI 1 "register_operand" " r") (match_operand:SI 2 "immediate_operand" " Iu05")) (match_operand:SI 3 "register_operand" " r")))] "TARGET_ISA_V3" "xor_slli\t%0, %3, %1, %2" [(set_attr "type" "alu") (set_attr "length" "4")]) (define_insn "*xor_srli" [(set (match_operand:SI 0 "register_operand" "= r") (xor:SI (lshiftrt:SI (match_operand:SI 1 "register_operand" " r") (match_operand:SI 2 "immediate_operand" " Iu05")) (match_operand:SI 3 "register_operand" " r")))] "TARGET_ISA_V3" "xor_srli\t%0, %3, %1, %2" [(set_attr "type" "alu") (set_attr "length" "4")]) ;; Rotate Right Instructions. (define_insn "rotrsi3" [(set (match_operand:SI 0 "register_operand" "= r, r") (rotatert:SI (match_operand:SI 1 "register_operand" " r, r") (match_operand:SI 2 "nonmemory_operand" " Iu05, r")))] "" "@ rotri\t%0, %1, %2 rotr\t%0, %1, %2" [(set_attr "type" "alu,alu") (set_attr "length" " 4, 4")]) ;; ---------------------------------------------------------------------------- ;; 'NEG' operation ;; ---------------------------------------------------------------------------- ;; For V3/V3M ISA, we have 'neg33' instruction. ;; So we can identify 'xor Rt3,Ra3' case and set its length to be 2. ;; And for V2 ISA, there is NO 'neg33' instruction. ;; The only option is to use 'subri A,B,0' (its semantic is 'A = 0 - B'). (define_insn "negsi2" [(set (match_operand:SI 0 "register_operand" "=w, r") (neg:SI (match_operand:SI 1 "register_operand" " w, r")))] "" "@ neg33\t%0, %1 subri\t%0, %1, 0" [(set_attr "type" "alu,alu") (set_attr "length" " 2, 4")]) ;; ---------------------------------------------------------------------------- ;; 'ONE_COMPLIMENT' operation ;; ---------------------------------------------------------------------------- ;; For V3/V3M ISA, we have 'not33' instruction. ;; So we can identify 'not Rt3,Ra3' case and set its length to be 2. (define_insn "one_cmplsi2" [(set (match_operand:SI 0 "register_operand" "=w, r") (not:SI (match_operand:SI 1 "register_operand" " w, r")))] "" "@ not33\t%0, %1 nor\t%0, %1, %1" [(set_attr "type" "alu,alu") (set_attr "length" " 2, 4")]) ;; ---------------------------------------------------------------------------- ;; Shift instructions. (define_insn "ashlsi3" [(set (match_operand:SI 0 "register_operand" "= l, r, r") (ashift:SI (match_operand:SI 1 "register_operand" " l, r, r") (match_operand:SI 2 "nonmemory_operand" " Iu03, Iu05, r")))] "" "@ slli333\t%0, %1, %2 slli\t%0, %1, %2 sll\t%0, %1, %2" [(set_attr "type" "alu,alu,alu") (set_attr "length" " 2, 4, 4")]) (define_insn "ashrsi3" [(set (match_operand:SI 0 "register_operand" "= d, r, r") (ashiftrt:SI (match_operand:SI 1 "register_operand" " 0, r, r") (match_operand:SI 2 "nonmemory_operand" " Iu05, Iu05, r")))] "" "@ srai45\t%0, %2 srai\t%0, %1, %2 sra\t%0, %1, %2" [(set_attr "type" "alu,alu,alu") (set_attr "length" " 2, 4, 4")]) (define_insn "lshrsi3" [(set (match_operand:SI 0 "register_operand" "= d, r, r") (lshiftrt:SI (match_operand:SI 1 "register_operand" " 0, r, r") (match_operand:SI 2 "nonmemory_operand" " Iu05, Iu05, r")))] "" "@ srli45\t%0, %2 srli\t%0, %1, %2 srl\t%0, %1, %2" [(set_attr "type" "alu,alu,alu") (set_attr "length" " 2, 4, 4")]) ;; ---------------------------------------------------------------------------- ;; ---------------------------------------------------------------------------- ;; Conditional Move patterns ;; ---------------------------------------------------------------------------- (define_expand "movsicc" [(set (match_operand:SI 0 "register_operand" "") (if_then_else:SI (match_operand 1 "comparison_operator" "") (match_operand:SI 2 "register_operand" "") (match_operand:SI 3 "register_operand" "")))] "TARGET_CMOV" { if ((GET_CODE (operands[1]) == EQ || GET_CODE (operands[1]) == NE) && GET_MODE (XEXP (operands[1], 0)) == SImode && XEXP (operands[1], 1) == const0_rtx) { /* If the operands[1] rtx is already (eq X 0) or (ne X 0), we have gcc generate original template rtx. */ goto create_template; } else { /* Since there is only 'slt'(Set when Less Than) instruction for comparison in Andes ISA, the major strategy we use here is to convert conditional move into 'LT + EQ' or 'LT + NE' rtx combination. We design constraints properly so that the reload phase will assist to make one source operand to use same register as result operand. Then we can use cmovz/cmovn to catch the other source operand which has different register. */ enum rtx_code code = GET_CODE (operands[1]); enum rtx_code new_code = code; rtx cmp_op0 = XEXP (operands[1], 0); rtx cmp_op1 = XEXP (operands[1], 1); rtx tmp; int reverse = 0; /* Main Goal: Use 'LT + EQ' or 'LT + NE' to target "then" part Strategy : Reverse condition and swap comparison operands For example: a <= b ? P : Q (LE or LEU) --> a > b ? Q : P (reverse condition) --> b < a ? Q : P (swap comparison operands to achieve 'LT/LTU') a >= b ? P : Q (GE or GEU) --> a < b ? Q : P (reverse condition to achieve 'LT/LTU') a < b ? P : Q (LT or LTU) --> (NO NEED TO CHANGE, it is already 'LT/LTU') a > b ? P : Q (GT or GTU) --> b < a ? P : Q (swap comparison operands to achieve 'LT/LTU') */ switch (code) { case NE: /* (a != b ? P : Q) can be expressed as (a == b ? Q : P) so, fall through to reverse condition */ case GE: case GEU: case LE: case LEU: new_code = reverse_condition (code); reverse = 1; break; case EQ: case GT: case GTU: case LT: case LTU: /* no need to reverse condition */ break; default: FAIL; } /* For '>' comparison operator, we swap operands so that we can have 'LT/LTU' operator. */ if (new_code == GT || new_code == GTU) { tmp = cmp_op0; cmp_op0 = cmp_op1; cmp_op1 = tmp; new_code = swap_condition (new_code); } /* Use a temporary register to store slt/slts result. */ tmp = gen_reg_rtx (SImode); /* Split EQ and NE because we don't have direct comparison of EQ and NE. If we don't split it, the conditional move transformation will fail when producing (SET A (EQ B C)) or (SET A (NE B C)). */ if (new_code == EQ) { emit_insn (gen_xorsi3 (tmp, cmp_op0, cmp_op1)); emit_insn (gen_slt_compare (tmp, tmp, GEN_INT (1))); } else if (new_code == NE) { emit_insn (gen_xorsi3 (tmp, cmp_op0, cmp_op1)); emit_insn (gen_slt_compare (tmp, GEN_INT (0), tmp)); } else /* This emit_insn will create corresponding 'slt/slts' insturction. */ emit_insn (gen_rtx_SET (tmp, gen_rtx_fmt_ee (new_code, SImode, cmp_op0, cmp_op1))); /* Change comparison semantic into (eq X 0) or (ne X 0) behavior so that cmovz or cmovn will be matched later. For reverse condition cases, we want to create a semantic that: (eq X 0) --> pick up "else" part For normal cases, we want to create a semantic that: (ne X 0) --> pick up "then" part Later we will have cmovz/cmovn instruction pattern to match corresponding behavior and output instruction. */ operands[1] = gen_rtx_fmt_ee (reverse ? EQ : NE, VOIDmode, tmp, const0_rtx); } create_template: do {} while(0); /* dummy line */ }) (define_insn "cmovz" [(set (match_operand:SI 0 "register_operand" "=r, r") (if_then_else:SI (eq (match_operand:SI 1 "register_operand" " r, r") (const_int 0)) (match_operand:SI 2 "register_operand" " r, 0") (match_operand:SI 3 "register_operand" " 0, r")))] "TARGET_CMOV" "@ cmovz\t%0, %2, %1 cmovn\t%0, %3, %1" [(set_attr "type" "move") (set_attr "length" "4")]) (define_insn "cmovn" [(set (match_operand:SI 0 "register_operand" "=r, r") (if_then_else:SI (ne (match_operand:SI 1 "register_operand" " r, r") (const_int 0)) (match_operand:SI 2 "register_operand" " r, 0") (match_operand:SI 3 "register_operand" " 0, r")))] "TARGET_CMOV" "@ cmovn\t%0, %2, %1 cmovz\t%0, %3, %1" [(set_attr "type" "move") (set_attr "length" "4")]) ;; ---------------------------------------------------------------------------- ;; Conditional Branch patterns ;; ---------------------------------------------------------------------------- (define_expand "cbranchsi4" [(set (pc) (if_then_else (match_operator 0 "comparison_operator" [(match_operand:SI 1 "register_operand" "") (match_operand:SI 2 "nds32_reg_constant_operand" "")]) (label_ref (match_operand 3 "" "")) (pc)))] "" { rtx tmp_reg; enum rtx_code code; code = GET_CODE (operands[0]); /* If operands[2] is (const_int 0), we can use beqz,bnez,bgtz,bgez,bltz,or blez instructions. So we have gcc generate original template rtx. */ if (GET_CODE (operands[2]) == CONST_INT) if (INTVAL (operands[2]) == 0) if ((code != GTU) && (code != GEU) && (code != LTU) && (code != LEU)) goto create_template; /* For other comparison, NDS32 ISA only has slt (Set-on-Less-Than) behavior for the comparison, we might need to generate other rtx patterns to achieve same semantic. */ switch (code) { case GT: case GTU: if (GET_CODE (operands[2]) == CONST_INT) { /* GT reg_A, const_int => !(LT reg_A, const_int + 1) */ tmp_reg = gen_rtx_REG (SImode, TA_REGNUM); /* We want to plus 1 into the integer value of operands[2] to create 'slt' instruction. This caculation is performed on the host machine, which may be 64-bit integer. So the meaning of caculation result may be different from the 32-bit nds32 target. For example: 0x7fffffff + 0x1 -> 0x80000000, this value is POSITIVE on 64-bit machine, but the expected value on 32-bit nds32 target should be NEGATIVE value. Hence, instead of using GEN_INT(), we use gen_int_mode() to explicitly create SImode constant rtx. */ operands[2] = gen_int_mode (INTVAL (operands[2]) + 1, SImode); if (code == GT) { /* GT, use slts instruction */ emit_insn (gen_slts_compare (tmp_reg, operands[1], operands[2])); } else { /* GTU, use slt instruction */ emit_insn (gen_slt_compare (tmp_reg, operands[1], operands[2])); } PUT_CODE (operands[0], EQ); operands[1] = tmp_reg; operands[2] = const0_rtx; emit_insn (gen_cbranchsi4 (operands[0], operands[1], operands[2], operands[3])); DONE; } else { /* GT reg_A, reg_B => LT reg_B, reg_A */ tmp_reg = gen_rtx_REG (SImode, TA_REGNUM); if (code == GT) { /* GT, use slts instruction */ emit_insn (gen_slts_compare (tmp_reg, operands[2], operands[1])); } else { /* GTU, use slt instruction */ emit_insn (gen_slt_compare (tmp_reg, operands[2], operands[1])); } PUT_CODE (operands[0], NE); operands[1] = tmp_reg; operands[2] = const0_rtx; emit_insn (gen_cbranchsi4 (operands[0], operands[1], operands[2], operands[3])); DONE; } case GE: case GEU: /* GE reg_A, reg_B => !(LT reg_A, reg_B) */ /* GE reg_A, const_int => !(LT reg_A, const_int) */ tmp_reg = gen_rtx_REG (SImode, TA_REGNUM); if (code == GE) { /* GE, use slts instruction */ emit_insn (gen_slts_compare (tmp_reg, operands[1], operands[2])); } else { /* GEU, use slt instruction */ emit_insn (gen_slt_compare (tmp_reg, operands[1], operands[2])); } PUT_CODE (operands[0], EQ); operands[1] = tmp_reg; operands[2] = const0_rtx; emit_insn (gen_cbranchsi4 (operands[0], operands[1], operands[2], operands[3])); DONE; case LT: case LTU: /* LT reg_A, reg_B => LT reg_A, reg_B */ /* LT reg_A, const_int => LT reg_A, const_int */ tmp_reg = gen_rtx_REG (SImode, TA_REGNUM); if (code == LT) { /* LT, use slts instruction */ emit_insn (gen_slts_compare (tmp_reg, operands[1], operands[2])); } else { /* LTU, use slt instruction */ emit_insn (gen_slt_compare (tmp_reg, operands[1], operands[2])); } PUT_CODE (operands[0], NE); operands[1] = tmp_reg; operands[2] = const0_rtx; emit_insn (gen_cbranchsi4 (operands[0], operands[1], operands[2], operands[3])); DONE; case LE: case LEU: if (GET_CODE (operands[2]) == CONST_INT) { /* LE reg_A, const_int => LT reg_A, const_int + 1 */ tmp_reg = gen_rtx_REG (SImode, TA_REGNUM); /* Note that (le:SI X INT_MAX) is not the same as (lt:SI X INT_MIN). We better have an assert here in case GCC does not properly optimize it away. The INT_MAX here is 0x7fffffff for target. */ gcc_assert (code != LE || INTVAL (operands[2]) != 0x7fffffff); operands[2] = gen_int_mode (INTVAL (operands[2]) + 1, SImode); if (code == LE) { /* LE, use slts instruction */ emit_insn (gen_slts_compare (tmp_reg, operands[1], operands[2])); } else { /* LEU, use slt instruction */ emit_insn (gen_slt_compare (tmp_reg, operands[1], operands[2])); } PUT_CODE (operands[0], NE); operands[1] = tmp_reg; operands[2] = const0_rtx; emit_insn (gen_cbranchsi4 (operands[0], operands[1], operands[2], operands[3])); DONE; } else { /* LE reg_A, reg_B => !(LT reg_B, reg_A) */ tmp_reg = gen_rtx_REG (SImode, TA_REGNUM); if (code == LE) { /* LE, use slts instruction */ emit_insn (gen_slts_compare (tmp_reg, operands[2], operands[1])); } else { /* LEU, use slt instruction */ emit_insn (gen_slt_compare (tmp_reg, operands[2], operands[1])); } PUT_CODE (operands[0], EQ); operands[1] = tmp_reg; operands[2] = const0_rtx; emit_insn (gen_cbranchsi4 (operands[0], operands[1], operands[2], operands[3])); DONE; } case EQ: case NE: /* NDS32 ISA has various form for eq/ne behavior no matter what kind of the operand is. So just generate original template rtx. */ goto create_template; default: FAIL; } create_template: do {} while(0); /* dummy line */ }) (define_insn "*cbranchsi4_equality_zero" [(set (pc) (if_then_else (match_operator 0 "nds32_equality_comparison_operator" [(match_operand:SI 1 "register_operand" "t, l, r") (const_int 0)]) (label_ref (match_operand 2 "" "")) (pc)))] "" { enum rtx_code code; code = GET_CODE (operands[0]); /* This zero-comparison conditional branch has two forms: 32-bit instruction => beqz/bnez imm16s << 1 16-bit instruction => beqzs8/bnezs8/beqz38/bnez38 imm8s << 1 For 32-bit case, we assume it is always reachable. (but check range -65500 ~ 65500) For 16-bit case, it must satisfy { 255 >= (label - pc) >= -256 } condition. However, since the $pc for nds32 is at the beginning of the instruction, we should leave some length space for current insn. So we use range -250 ~ 250. */ switch (get_attr_length (insn)) { case 2: if (which_alternative == 0) { /* constraint: t */ return (code == EQ) ? "beqzs8\t%2" : "bnezs8\t%2"; } else if (which_alternative == 1) { /* constraint: l */ return (code == EQ) ? "beqz38\t%1, %2" : "bnez38\t%1, %2"; } else { /* constraint: r */ /* For which_alternative==2, it should not be here. */ gcc_unreachable (); } case 4: /* including constraints: t, l, and r */ return (code == EQ) ? "beqz\t%1, %2" : "bnez\t%1, %2"; case 6: if (which_alternative == 0) { /* constraint: t */ if (code == EQ) { /* beqzs8 .L0 => bnezs8 .LCB0 j .L0 .LCB0: */ return "bnezs8\t.LCB%=\;j\t%2\n.LCB%=:"; } else { /* bnezs8 .L0 => beqzs8 .LCB0 j .L0 .LCB0: */ return "beqzs8\t.LCB%=\;j\t%2\n.LCB%=:"; } } else if (which_alternative == 1) { /* constraint: l */ if (code == EQ) { /* beqz38 $r0, .L0 => bnez38 $r0, .LCB0 j .L0 .LCB0: */ return "bnez38\t%1, .LCB%=\;j\t%2\n.LCB%=:"; } else { /* bnez38 $r0, .L0 => beqz38 $r0, .LCB0 j .L0 .LCB0: */ return "beqz38\t%1, .LCB%=\;j\t%2\n.LCB%=:"; } } else { /* constraint: r */ /* For which_alternative==2, it should not be here. */ gcc_unreachable (); } case 8: /* constraint: t, l, r. */ if (code == EQ) { /* beqz $r8, .L0 => bnez $r8, .LCB0 j .L0 .LCB0: */ return "bnez\t%1, .LCB%=\;j\t%2\n.LCB%=:"; } else { /* bnez $r8, .L0 => beqz $r8, .LCB0 j .L0 .LCB0: */ return "beqz\t%1, .LCB%=\;j\t%2\n.LCB%=:"; } default: gcc_unreachable (); } } [(set_attr "type" "branch") (set_attr "enabled" "1") (set_attr_alternative "length" [ ;; Alternative 0 (if_then_else (and (ge (minus (match_dup 2) (pc)) (const_int -250)) (le (minus (match_dup 2) (pc)) (const_int 250))) (if_then_else (match_test "TARGET_16_BIT") (const_int 2) (const_int 4)) (if_then_else (and (ge (minus (match_dup 2) (pc)) (const_int -65500)) (le (minus (match_dup 2) (pc)) (const_int 65500))) (const_int 4) (if_then_else (match_test "TARGET_16_BIT") (const_int 6) (const_int 8)))) ;; Alternative 1 (if_then_else (and (ge (minus (match_dup 2) (pc)) (const_int -250)) (le (minus (match_dup 2) (pc)) (const_int 250))) (if_then_else (match_test "TARGET_16_BIT") (const_int 2) (const_int 4)) (if_then_else (and (ge (minus (match_dup 2) (pc)) (const_int -65500)) (le (minus (match_dup 2) (pc)) (const_int 65500))) (const_int 4) (if_then_else (match_test "TARGET_16_BIT") (const_int 6) (const_int 8)))) ;; Alternative 2 (if_then_else (and (ge (minus (match_dup 2) (pc)) (const_int -65500)) (le (minus (match_dup 2) (pc)) (const_int 65500))) (const_int 4) (const_int 8)) ])]) ;; This pattern is dedicated to V2 ISA, ;; because V2 DOES NOT HAVE beqc/bnec instruction. (define_insn "*cbranchsi4_equality_reg" [(set (pc) (if_then_else (match_operator 0 "nds32_equality_comparison_operator" [(match_operand:SI 1 "register_operand" "r") (match_operand:SI 2 "nds32_reg_constant_operand" "r")]) (label_ref (match_operand 3 "" "")) (pc)))] "TARGET_ISA_V2" { enum rtx_code code; code = GET_CODE (operands[0]); /* This register-comparison conditional branch has one form: 32-bit instruction => beq/bne imm14s << 1 For 32-bit case, we assume it is always reachable. (but check range -16350 ~ 16350). */ switch (code) { case EQ: /* r, r */ switch (get_attr_length (insn)) { case 4: return "beq\t%1, %2, %3"; case 8: /* beq $r0, $r1, .L0 => bne $r0, $r1, .LCB0 j .L0 .LCB0: */ return "bne\t%1, %2, .LCB%=\;j\t%3\n.LCB%=:"; default: gcc_unreachable (); } case NE: /* r, r */ switch (get_attr_length (insn)) { case 4: return "bne\t%1, %2, %3"; case 8: /* bne $r0, $r1, .L0 => beq $r0, $r1, .LCB0 j .L0 .LCB0: */ return "beq\t%1, %2, .LCB%=\;j\t%3\n.LCB%=:"; default: gcc_unreachable (); } default: gcc_unreachable (); } } [(set_attr "type" "branch") (set (attr "length") (if_then_else (and (ge (minus (match_dup 3) (pc)) (const_int -16350)) (le (minus (match_dup 3) (pc)) (const_int 16350))) (const_int 4) (const_int 8)))]) ;; This pattern is dedicated to V3/V3M, ;; because V3/V3M DO HAVE beqc/bnec instruction. (define_insn "*cbranchsi4_equality_reg_or_const_int" [(set (pc) (if_then_else (match_operator 0 "nds32_equality_comparison_operator" [(match_operand:SI 1 "register_operand" "r, r") (match_operand:SI 2 "nds32_reg_constant_operand" "r, Is11")]) (label_ref (match_operand 3 "" "")) (pc)))] "TARGET_ISA_V3 || TARGET_ISA_V3M" { enum rtx_code code; code = GET_CODE (operands[0]); /* This register-comparison conditional branch has one form: 32-bit instruction => beq/bne imm14s << 1 32-bit instruction => beqc/bnec imm8s << 1 For 32-bit case, we assume it is always reachable. (but check range -16350 ~ 16350 and -250 ~ 250). */ switch (code) { case EQ: if (which_alternative == 0) { /* r, r */ switch (get_attr_length (insn)) { case 4: return "beq\t%1, %2, %3"; case 8: /* beq $r0, $r1, .L0 => bne $r0, $r1, .LCB0 j .L0 .LCB0: */ return "bne\t%1, %2, .LCB%=\;j\t%3\n.LCB%=:"; default: gcc_unreachable (); } } else { /* r, Is11 */ switch (get_attr_length (insn)) { case 4: return "beqc\t%1, %2, %3"; case 8: /* beqc $r0, constant, .L0 => bnec $r0, constant, .LCB0 j .L0 .LCB0: */ return "bnec\t%1, %2, .LCB%=\;j\t%3\n.LCB%=:"; default: gcc_unreachable (); } } case NE: if (which_alternative == 0) { /* r, r */ switch (get_attr_length (insn)) { case 4: return "bne\t%1, %2, %3"; case 8: /* bne $r0, $r1, .L0 => beq $r0, $r1, .LCB0 j .L0 .LCB0: */ return "beq\t%1, %2, .LCB%=\;j\t%3\n.LCB%=:"; default: gcc_unreachable (); } } else { /* r, Is11 */ switch (get_attr_length (insn)) { case 4: return "bnec\t%1, %2, %3"; case 8: /* bnec $r0, constant, .L0 => beqc $r0, constant, .LCB0 j .L0 .LCB0: */ return "beqc\t%1, %2, .LCB%=\;j\t%3\n.LCB%=:"; default: gcc_unreachable (); } } default: gcc_unreachable (); } } [(set_attr "type" "branch") (set_attr_alternative "length" [ ;; Alternative 0 (if_then_else (and (ge (minus (match_dup 3) (pc)) (const_int -16350)) (le (minus (match_dup 3) (pc)) (const_int 16350))) (const_int 4) (const_int 8)) ;; Alternative 1 (if_then_else (and (ge (minus (match_dup 3) (pc)) (const_int -250)) (le (minus (match_dup 3) (pc)) (const_int 250))) (const_int 4) (const_int 8)) ])]) (define_insn "*cbranchsi4_greater_less_zero" [(set (pc) (if_then_else (match_operator 0 "nds32_greater_less_comparison_operator" [(match_operand:SI 1 "register_operand" "r") (const_int 0)]) (label_ref (match_operand 2 "" "")) (pc)))] "" { enum rtx_code code; code = GET_CODE (operands[0]); /* This zero-greater-less-comparison conditional branch has one form: 32-bit instruction => bgtz/bgez/bltz/blez imm16s << 1 For 32-bit case, we assume it is always reachable. (but check range -65500 ~ 65500). */ if (get_attr_length (insn) == 8) { /* The branch target is too far to simply use one bgtz/bgez/bltz/blez instruction. We need to reverse condition and use 'j' to jump to the target. */ switch (code) { case GT: /* bgtz $r8, .L0 => blez $r8, .LCB0 j .L0 .LCB0: */ return "blez\t%1, .LCB%=\;j\t%2\n.LCB%=:"; case GE: /* bgez $r8, .L0 => bltz $r8, .LCB0 j .L0 .LCB0: */ return "bltz\t%1, .LCB%=\;j\t%2\n.LCB%=:"; case LT: /* bltz $r8, .L0 => bgez $r8, .LCB0 j .L0 .LCB0: */ return "bgez\t%1, .LCB%=\;j\t%2\n.LCB%=:"; case LE: /* blez $r8, .L0 => bgtz $r8, .LCB0 j .L0 .LCB0: */ return "bgtz\t%1, .LCB%=\;j\t%2\n.LCB%=:"; default: gcc_unreachable (); } } switch (code) { case GT: return "bgtz\t%1, %2"; case GE: return "bgez\t%1, %2"; case LT: return "bltz\t%1, %2"; case LE: return "blez\t%1, %2"; default: gcc_unreachable (); } } [(set_attr "type" "branch") (set (attr "length") (if_then_else (and (ge (minus (match_dup 2) (pc)) (const_int -65500)) (le (minus (match_dup 2) (pc)) (const_int 65500))) (const_int 4) (const_int 8)))]) (define_expand "cstoresi4" [(set (match_operand:SI 0 "register_operand" "") (match_operator:SI 1 "comparison_operator" [(match_operand:SI 2 "register_operand" "") (match_operand:SI 3 "nonmemory_operand" "")]))] "" { rtx tmp_reg; enum rtx_code code; code = GET_CODE (operands[1]); switch (code) { case EQ: if (GET_CODE (operands[3]) == CONST_INT) { /* reg_R = (reg_A == const_int_B) --> addi reg_C, reg_A, -const_int_B slti reg_R, reg_C, const_int_1 */ tmp_reg = gen_reg_rtx (SImode); operands[3] = gen_int_mode (-INTVAL (operands[3]), SImode); /* If the integer value is not in the range of imm15s, we need to force register first because our addsi3 pattern only accept nds32_rimm15s_operand predicate. */ if (!satisfies_constraint_Is15 (operands[3])) operands[3] = force_reg (SImode, operands[3]); emit_insn (gen_addsi3 (tmp_reg, operands[2], operands[3])); emit_insn (gen_slt_compare (operands[0], tmp_reg, const1_rtx)); DONE; } else { /* reg_R = (reg_A == reg_B) --> xor reg_C, reg_A, reg_B slti reg_R, reg_C, const_int_1 */ tmp_reg = gen_reg_rtx (SImode); emit_insn (gen_xorsi3 (tmp_reg, operands[2], operands[3])); emit_insn (gen_slt_compare (operands[0], tmp_reg, const1_rtx)); DONE; } case NE: if (GET_CODE (operands[3]) == CONST_INT) { /* reg_R = (reg_A != const_int_B) --> addi reg_C, reg_A, -const_int_B slti reg_R, const_int_0, reg_C */ tmp_reg = gen_reg_rtx (SImode); operands[3] = gen_int_mode (-INTVAL (operands[3]), SImode); /* If the integer value is not in the range of imm15s, we need to force register first because our addsi3 pattern only accept nds32_rimm15s_operand predicate. */ if (!satisfies_constraint_Is15 (operands[3])) operands[3] = force_reg (SImode, operands[3]); emit_insn (gen_addsi3 (tmp_reg, operands[2], operands[3])); emit_insn (gen_slt_compare (operands[0], const0_rtx, tmp_reg)); DONE; } else { /* reg_R = (reg_A != reg_B) --> xor reg_C, reg_A, reg_B slti reg_R, const_int_0, reg_C */ tmp_reg = gen_reg_rtx (SImode); emit_insn (gen_xorsi3 (tmp_reg, operands[2], operands[3])); emit_insn (gen_slt_compare (operands[0], const0_rtx, tmp_reg)); DONE; } case GT: case GTU: /* reg_R = (reg_A > reg_B) --> slt reg_R, reg_B, reg_A */ /* reg_R = (reg_A > const_int_B) --> slt reg_R, const_int_B, reg_A */ if (code == GT) { /* GT, use slts instruction */ emit_insn (gen_slts_compare (operands[0], operands[3], operands[2])); } else { /* GTU, use slt instruction */ emit_insn (gen_slt_compare (operands[0], operands[3], operands[2])); } DONE; case GE: case GEU: if (GET_CODE (operands[3]) == CONST_INT) { /* reg_R = (reg_A >= const_int_B) --> movi reg_C, const_int_B - 1 slt reg_R, reg_C, reg_A */ tmp_reg = gen_reg_rtx (SImode); emit_insn (gen_movsi (tmp_reg, gen_int_mode (INTVAL (operands[3]) - 1, SImode))); if (code == GE) { /* GE, use slts instruction */ emit_insn (gen_slts_compare (operands[0], tmp_reg, operands[2])); } else { /* GEU, use slt instruction */ emit_insn (gen_slt_compare (operands[0], tmp_reg, operands[2])); } DONE; } else { /* reg_R = (reg_A >= reg_B) --> slt reg_R, reg_A, reg_B xori reg_R, reg_R, const_int_1 */ if (code == GE) { /* GE, use slts instruction */ emit_insn (gen_slts_compare (operands[0], operands[2], operands[3])); } else { /* GEU, use slt instruction */ emit_insn (gen_slt_compare (operands[0], operands[2], operands[3])); } /* perform 'not' behavior */ emit_insn (gen_xorsi3 (operands[0], operands[0], const1_rtx)); DONE; } case LT: case LTU: /* reg_R = (reg_A < reg_B) --> slt reg_R, reg_A, reg_B */ /* reg_R = (reg_A < const_int_B) --> slt reg_R, reg_A, const_int_B */ if (code == LT) { /* LT, use slts instruction */ emit_insn (gen_slts_compare (operands[0], operands[2], operands[3])); } else { /* LTU, use slt instruction */ emit_insn (gen_slt_compare (operands[0], operands[2], operands[3])); } DONE; case LE: case LEU: if (GET_CODE (operands[3]) == CONST_INT) { /* reg_R = (reg_A <= const_int_B) --> movi reg_C, const_int_B + 1 slt reg_R, reg_A, reg_C */ tmp_reg = gen_reg_rtx (SImode); emit_insn (gen_movsi (tmp_reg, gen_int_mode (INTVAL (operands[3]) + 1, SImode))); if (code == LE) { /* LE, use slts instruction */ emit_insn (gen_slts_compare (operands[0], operands[2], tmp_reg)); } else { /* LEU, use slt instruction */ emit_insn (gen_slt_compare (operands[0], operands[2], tmp_reg)); } DONE; } else { /* reg_R = (reg_A <= reg_B) --> slt reg_R, reg_B, reg_A xori reg_R, reg_R, const_int_1 */ if (code == LE) { /* LE, use slts instruction */ emit_insn (gen_slts_compare (operands[0], operands[3], operands[2])); } else { /* LEU, use slt instruction */ emit_insn (gen_slt_compare (operands[0], operands[3], operands[2])); } /* perform 'not' behavior */ emit_insn (gen_xorsi3 (operands[0], operands[0], const1_rtx)); DONE; } default: gcc_unreachable (); } }) (define_insn "slts_compare" [(set (match_operand:SI 0 "register_operand" "=t, t, r, r") (lt:SI (match_operand:SI 1 "nonmemory_operand" " d, d, r, r") (match_operand:SI 2 "nonmemory_operand" " r, Iu05, r, Is15")))] "" "@ slts45\t%1, %2 sltsi45\t%1, %2 slts\t%0, %1, %2 sltsi\t%0, %1, %2" [(set_attr "type" "compare,compare,compare,compare") (set_attr "length" " 2, 2, 4, 4")]) (define_insn "slt_compare" [(set (match_operand:SI 0 "register_operand" "=t, t, r, r") (ltu:SI (match_operand:SI 1 "nonmemory_operand" " d, d, r, r") (match_operand:SI 2 "nonmemory_operand" " r, Iu05, r, Is15")))] "" "@ slt45\t%1, %2 slti45\t%1, %2 slt\t%0, %1, %2 slti\t%0, %1, %2" [(set_attr "type" "compare,compare,compare,compare") (set_attr "length" " 2, 2, 4, 4")]) ;; ---------------------------------------------------------------------------- ;; Unconditional and other jump instructions. (define_insn "jump" [(set (pc) (label_ref (match_operand 0 "" "")))] "" { /* This unconditional jump has two forms: 32-bit instruction => j imm24s << 1 16-bit instruction => j8 imm8s << 1 For 32-bit case, we assume it is always reachable. For 16-bit case, it must satisfy { 255 >= (label - pc) >= -256 } condition. However, since the $pc for nds32 is at the beginning of the instruction, we should leave some length space for current insn. So we use range -250 ~ 250. */ switch (get_attr_length (insn)) { case 2: return "j8\t%0"; case 4: return "j\t%0"; default: gcc_unreachable (); } } [(set_attr "type" "branch") (set_attr "enabled" "1") (set (attr "length") (if_then_else (and (ge (minus (match_dup 0) (pc)) (const_int -250)) (le (minus (match_dup 0) (pc)) (const_int 250))) (if_then_else (match_test "TARGET_16_BIT") (const_int 2) (const_int 4)) (const_int 4)))]) (define_insn "indirect_jump" [(set (pc) (match_operand:SI 0 "register_operand" "r, r"))] "" "@ jr5\t%0 jr\t%0" [(set_attr "type" "branch,branch") (set_attr "length" " 2, 4")]) ;; Subroutine call instruction returning no value. ;; operands[0]: It should be a mem RTX whose address is ;; the address of the function. ;; operands[1]: It is the number of bytes of arguments pushed as a const_int. ;; operands[2]: It is the number of registers used as operands. (define_expand "call" [(parallel [(call (match_operand 0 "memory_operand" "") (match_operand 1)) (clobber (reg:SI LP_REGNUM)) (clobber (reg:SI TA_REGNUM))])] "" "" ) (define_insn "*call_register" [(parallel [(call (mem (match_operand:SI 0 "register_operand" "r, r")) (match_operand 1)) (clobber (reg:SI LP_REGNUM)) (clobber (reg:SI TA_REGNUM))])] "" "@ jral5\t%0 jral\t%0" [(set_attr "type" "branch,branch") (set_attr "length" " 2, 4")]) (define_insn "*call_immediate" [(parallel [(call (mem (match_operand:SI 0 "immediate_operand" "i")) (match_operand 1)) (clobber (reg:SI LP_REGNUM)) (clobber (reg:SI TA_REGNUM))])] "" { if (TARGET_CMODEL_LARGE) return "bal\t%0"; else return "jal\t%0"; } [(set_attr "type" "branch") (set (attr "length") (if_then_else (match_test "TARGET_CMODEL_LARGE") (const_int 12) (const_int 4)))]) ;; Subroutine call instruction returning a value. ;; operands[0]: It is the hard regiser in which the value is returned. ;; The rest three operands are the same as the ;; three operands of the 'call' instruction. ;; (but with numbers increased by one) (define_expand "call_value" [(parallel [(set (match_operand 0) (call (match_operand 1 "memory_operand" "") (match_operand 2))) (clobber (reg:SI LP_REGNUM)) (clobber (reg:SI TA_REGNUM))])] "" "" ) (define_insn "*call_value_register" [(parallel [(set (match_operand 0) (call (mem (match_operand:SI 1 "register_operand" "r, r")) (match_operand 2))) (clobber (reg:SI LP_REGNUM)) (clobber (reg:SI TA_REGNUM))])] "" "@ jral5\t%1 jral\t%1" [(set_attr "type" "branch,branch") (set_attr "length" " 2, 4")]) (define_insn "*call_value_immediate" [(parallel [(set (match_operand 0) (call (mem (match_operand:SI 1 "immediate_operand" "i")) (match_operand 2))) (clobber (reg:SI LP_REGNUM)) (clobber (reg:SI TA_REGNUM))])] "" { if (TARGET_CMODEL_LARGE) return "bal\t%1"; else return "jal\t%1"; } [(set_attr "type" "branch") (set (attr "length") (if_then_else (match_test "TARGET_CMODEL_LARGE") (const_int 12) (const_int 4)))]) ;; ---------------------------------------------------------------------------- ;; The sibcall patterns. ;; sibcall ;; sibcall_register ;; sibcall_immediate (define_expand "sibcall" [(parallel [(call (match_operand 0 "memory_operand" "") (const_int 0)) (clobber (reg:SI TA_REGNUM)) (return)])] "" "" ) (define_insn "*sibcall_register" [(parallel [(call (mem (match_operand:SI 0 "register_operand" "r, r")) (match_operand 1)) (clobber (reg:SI TA_REGNUM)) (return)])] "" "@ jr5\t%0 jr\t%0" [(set_attr "type" "branch,branch") (set_attr "length" " 2, 4")]) (define_insn "*sibcall_immediate" [(parallel [(call (mem (match_operand:SI 0 "immediate_operand" "i")) (match_operand 1)) (clobber (reg:SI TA_REGNUM)) (return)])] "" { if (TARGET_CMODEL_LARGE) return "b\t%0"; else return "j\t%0"; } [(set_attr "type" "branch") (set (attr "length") (if_then_else (match_test "TARGET_CMODEL_LARGE") (const_int 12) (const_int 4)))]) ;; sibcall_value ;; sibcall_value_register ;; sibcall_value_immediate (define_expand "sibcall_value" [(parallel [(set (match_operand 0) (call (match_operand 1 "memory_operand" "") (const_int 0))) (clobber (reg:SI TA_REGNUM)) (return)])] "" "" ) (define_insn "*sibcall_value_register" [(parallel [(set (match_operand 0) (call (mem (match_operand:SI 1 "register_operand" "r, r")) (match_operand 2))) (clobber (reg:SI TA_REGNUM)) (return)])] "" "@ jr5\t%1 jr\t%1" [(set_attr "type" "branch,branch") (set_attr "length" " 2, 4")]) (define_insn "*sibcall_value_immediate" [(parallel [(set (match_operand 0) (call (mem (match_operand:SI 1 "immediate_operand" "i")) (match_operand 2))) (clobber (reg:SI TA_REGNUM)) (return)])] "" { if (TARGET_CMODEL_LARGE) return "b\t%1"; else return "j\t%1"; } [(set_attr "type" "branch") (set (attr "length") (if_then_else (match_test "TARGET_CMODEL_LARGE") (const_int 12) (const_int 4)))]) ;; ---------------------------------------------------------------------------- ;; prologue and epilogue. (define_expand "prologue" [(const_int 0)] "" { /* Note that only under V3/V3M ISA, we could use v3push prologue. In addition, we do not want to use v3push for isr function and variadic function. */ if (TARGET_V3PUSH && !nds32_isr_function_p (current_function_decl) && (cfun->machine->va_args_size == 0)) nds32_expand_prologue_v3push (); else nds32_expand_prologue (); DONE; }) (define_expand "epilogue" [(const_int 0)] "" { /* Note that only under V3/V3M ISA, we could use v3pop epilogue. In addition, we do not want to use v3pop for isr function and variadic function. */ if (TARGET_V3PUSH && !nds32_isr_function_p (current_function_decl) && (cfun->machine->va_args_size == 0)) nds32_expand_epilogue_v3pop (false); else nds32_expand_epilogue (false); DONE; }) (define_expand "sibcall_epilogue" [(const_int 0)] "" { /* Pass true to indicate that this is sibcall epilogue and exit from a function without the final branch back to the calling function. */ if (TARGET_V3PUSH && !nds32_isr_function_p (current_function_decl)) nds32_expand_epilogue_v3pop (true); else nds32_expand_epilogue (true); DONE; }) ;; nop instruction. (define_insn "nop" [(const_int 0)] "" { if (TARGET_16_BIT) return "nop16"; else return "nop"; } [(set_attr "type" "misc") (set_attr "enabled" "1") (set (attr "length") (if_then_else (match_test "TARGET_16_BIT") (const_int 2) (const_int 4)))]) ;; ---------------------------------------------------------------------------- ;; Stack push/pop operations ;; ---------------------------------------------------------------------------- ;; The pattern for stack push. ;; Both stack_push_multiple and stack_v3push use the following pattern. ;; So we need to use TARGET_V3PUSH to determine the instruction length. (define_insn "*stack_push" [(match_parallel 0 "nds32_stack_push_operation" [(set (mem:SI (plus:SI (reg:SI SP_REGNUM) (match_operand:SI 1 "const_int_operand" ""))) (match_operand:SI 2 "register_operand" "")) ])] "" { return nds32_output_stack_push (operands[0]); } [(set_attr "type" "misc") (set_attr "enabled" "1") (set (attr "length") (if_then_else (match_test "TARGET_V3PUSH && !nds32_isr_function_p (cfun->decl) && (cfun->machine->va_args_size == 0)") (const_int 2) (const_int 4)))]) ;; The pattern for stack pop. ;; Both stack_pop_multiple and stack_v3pop use the following pattern. ;; So we need to use TARGET_V3PUSH to determine the instruction length. (define_insn "*stack_pop" [(match_parallel 0 "nds32_stack_pop_operation" [(set (match_operand:SI 1 "register_operand" "") (mem:SI (reg:SI SP_REGNUM))) ])] "" { return nds32_output_stack_pop (operands[0]); } [(set_attr "type" "misc") (set_attr "enabled" "1") (set (attr "length") (if_then_else (match_test "TARGET_V3PUSH && !nds32_isr_function_p (cfun->decl) && (cfun->machine->va_args_size == 0)") (const_int 2) (const_int 4)))]) ;; ---------------------------------------------------------------------------- ;; Return operation patterns ;; ---------------------------------------------------------------------------- ;; Use this pattern to expand a return instruction ;; with simple_return rtx if no epilogue is required. (define_expand "return" [(simple_return)] "nds32_can_use_return_insn ()" "" ) ;; This pattern is expanded only by the shrink-wrapping optimization ;; on paths where the function prologue has not been executed. (define_expand "simple_return" [(simple_return)] "" "" ) (define_insn "return_internal" [(simple_return)] "" { if (TARGET_16_BIT) return "ret5"; else return "ret"; } [(set_attr "type" "branch") (set_attr "enabled" "1") (set (attr "length") (if_then_else (match_test "TARGET_16_BIT") (const_int 2) (const_int 4)))]) ;; ---------------------------------------------------------------------------- ;; Jump Table patterns ;; ---------------------------------------------------------------------------- ;; Need to implement ASM_OUTPUT_ADDR_VEC_ELT (for normal jump table) ;; or ASM_OUTPUT_ADDR_DIFF_ELT (for pc relative jump table) as well. ;; ;; operands[0]: The index to dispatch on. ;; operands[1]: The lower bound for indices in the table. ;; operands[2]: The total range of indices int the table. ;; i.e. The largest index minus the smallest one. ;; operands[3]: A label that precedes the table itself. ;; operands[4]: A label to jump to if the index has a value outside the bounds. ;; ;; We need to create following sequences for jump table code generation: ;; A) k <-- (plus (operands[0]) (-operands[1])) ;; B) if (gtu k operands[2]) then goto operands[4] ;; C) t <-- operands[3] ;; D) z <-- (mem (plus (k << 0 or 1 or 2) t)) ;; E) z <-- t + z (NOTE: This is only required for pc relative jump table.) ;; F) jump to target with register t or z ;; ;; The steps C, D, E, and F are performed by casesi_internal pattern. (define_expand "casesi" [(match_operand:SI 0 "register_operand" "r") ; index to jump on (match_operand:SI 1 "immediate_operand" "i") ; lower bound (match_operand:SI 2 "immediate_operand" "i") ; total range (match_operand:SI 3 "" "") ; table label (match_operand:SI 4 "" "")] ; Out of range label "" { rtx add_tmp; rtx reg, test; /* Step A: "k <-- (plus (operands[0]) (-operands[1]))". */ if (operands[1] != const0_rtx) { reg = gen_reg_rtx (SImode); add_tmp = gen_int_mode (-INTVAL (operands[1]), SImode); /* If the integer value is not in the range of imm15s, we need to force register first because our addsi3 pattern only accept nds32_rimm15s_operand predicate. */ add_tmp = force_reg (SImode, add_tmp); emit_insn (gen_addsi3 (reg, operands[0], add_tmp)); operands[0] = reg; } /* Step B: "if (gtu k operands[2]) then goto operands[4]". */ test = gen_rtx_GTU (VOIDmode, operands[0], operands[2]); emit_jump_insn (gen_cbranchsi4 (test, operands[0], operands[2], operands[4])); /* Step C, D, E, and F, using another temporary register. */ rtx tmp = gen_reg_rtx (SImode); emit_jump_insn (gen_casesi_internal (operands[0], operands[3], tmp)); DONE; }) ;; We are receiving operands from casesi pattern: ;; ;; operands[0]: The index that have been substracted with lower bound. ;; operands[1]: A label that precedes the table itself. ;; operands[2]: A temporary register to retrieve value in table. ;; ;; We need to perform steps C, D, E, and F: ;; ;; C) t <-- operands[1] ;; D) z <-- (mem (plus (operands[0] << m) t)) ;; m is 2 for normal jump table. ;; m is 0, 1, or 2 for pc relative jump table based on diff size. ;; E) t <-- z + t (NOTE: This is only required for pc relative jump table.) ;; F) Jump to target with register t or z. ;; ;; The USE in this pattern is needed to tell flow analysis that this is ;; a CASESI insn. It has no other purpose. (define_insn "casesi_internal" [(parallel [(set (pc) (mem:SI (plus:SI (mult:SI (match_operand:SI 0 "register_operand" "r") (const_int 4)) (label_ref (match_operand 1 "" ""))))) (use (label_ref (match_dup 1))) (clobber (match_operand:SI 2 "register_operand" "=r")) (clobber (reg:SI TA_REGNUM))])] "" { if (CASE_VECTOR_PC_RELATIVE) return nds32_output_casesi_pc_relative (operands); else return nds32_output_casesi (operands); } [(set_attr "length" "20") (set_attr "type" "alu")]) ;; ---------------------------------------------------------------------------- ;; Performance Extension (define_insn "clzsi2" [(set (match_operand:SI 0 "register_operand" "=r") (clz:SI (match_operand:SI 1 "register_operand" " r")))] "TARGET_PERF_EXT" "clz\t%0, %1" [(set_attr "type" "alu") (set_attr "length" "4")]) (define_insn "smaxsi3" [(set (match_operand:SI 0 "register_operand" "=r") (smax:SI (match_operand:SI 1 "register_operand" " r") (match_operand:SI 2 "register_operand" " r")))] "TARGET_PERF_EXT" "max\t%0, %1, %2" [(set_attr "type" "alu") (set_attr "length" "4")]) (define_insn "sminsi3" [(set (match_operand:SI 0 "register_operand" "=r") (smin:SI (match_operand:SI 1 "register_operand" " r") (match_operand:SI 2 "register_operand" " r")))] "TARGET_PERF_EXT" "min\t%0, %1, %2" [(set_attr "type" "alu") (set_attr "length" "4")]) (define_insn "*btst" [(set (match_operand:SI 0 "register_operand" "= r") (zero_extract:SI (match_operand:SI 1 "register_operand" " r") (const_int 1) (match_operand:SI 2 "immediate_operand" " Iu05")))] "TARGET_PERF_EXT" "btst\t%0, %1, %2" [(set_attr "type" "alu") (set_attr "length" "4")]) ;; ---------------------------------------------------------------------------- ;; Pseudo NOPs (define_insn "pop25return" [(return) (unspec_volatile:SI [(reg:SI LP_REGNUM)] UNSPEC_VOLATILE_POP25_RETURN)] "" "! return for pop 25" [(set_attr "length" "0")] ) ;; ----------------------------------------------------------------------------