| ;; GCC machine description for CR16. |
| ;; Copyright (C) 2012-2022 Free Software Foundation, Inc. |
| ;; Contributed by KPIT Cummins Infosystems Limited. |
| |
| ;; This file is part of GCC. |
| |
| ;; GCC is free software; you can redistribute it and/or modify it |
| ;; under the terms of the GNU General Public License as published by |
| ;; the Free Software Foundation; either version 3, or (at your option) |
| ;; any later version. |
| |
| ;; GCC is distributed in the hope that it will be useful, but WITHOUT |
| ;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY |
| ;; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public |
| ;; License for more details. |
| |
| ;; You should have received a copy of the GNU General Public License |
| ;; along with GCC; see the file COPYING3. If not see |
| ;; <http://www.gnu.org/licenses/>. |
| |
| ;; Register numbers |
| (define_constants |
| [(SP_REGNUM 15); Stack pointer |
| (RA_REGNUM 14); Return address |
| ] |
| ) |
| |
| ;; Predicates & Constraints |
| (include "predicates.md") |
| (include "constraints.md") |
| |
| ;; UNSPEC usage |
| (define_constants |
| [(UNSPEC_PIC_ADDR 0) |
| (UNSPEC_PIC_LOAD_ADDR 1) |
| (UNSPEC_LIBRARY_OFFSET 2) |
| (UNSPEC_SH_LIB_PUSH_R12 3) |
| (UNSPEC_SH_LIB_POP_R12 4) |
| (UNSPEC_RETURN_ADDR 5) |
| ] |
| ) |
| |
| ;; Attributes |
| (define_attr "length" "" (const_int 2)) |
| |
| (define_asm_attributes |
| [(set_attr "length" "2")] |
| ) |
| |
| ;; Mode Macro Definitions |
| (define_mode_iterator CR16IM [QI HI SI]) |
| (define_mode_iterator LONG [SI SF]) |
| (define_mode_iterator ALLMTD [QI HI SI SF DI DF]) |
| (define_mode_iterator DOUBLE [DI DF]) |
| (define_mode_iterator SHORT [QI HI]) |
| (define_mode_attr tIsa [(QI "b") (HI "w") (SI "d") (SF "d")]) |
| (define_mode_attr lImmArith [(QI "4") (HI "4") (SI "6") (SF "6")]) |
| (define_mode_attr lImmArithD [(QI "4") (HI "4") (SI "6") (SF "6") (DI "12") (DF "12")]) |
| (define_mode_attr iF [(QI "i") (HI "i") (SI "i") (SF "F")]) |
| (define_mode_attr iFD [(DI "i") (DF "F")]) |
| (define_mode_attr LL [(QI "L") (HI "L")]) |
| (define_mode_attr shImmBits [(QI "3") (HI "4") (SI "5")]) |
| |
| ; In QI mode we push 2 bytes instead of 1 byte. |
| (define_mode_attr pushCnstr [(QI "X") (HI "<") (SI "<") (SF "<") (DI "<") (DF "<")]) |
| |
| ; tpush will be used to generate the 'number of registers to push' in the |
| ; push instruction. |
| (define_mode_attr tpush [(QI "1") (HI "1") (SI "2") (SF "2") (DI "4") (DF "4")]) |
| |
| ;; Code Macro Definitions |
| (define_code_attr sIsa [(sign_extend "") (zero_extend "u")]) |
| (define_code_attr sPat [(sign_extend "s") (zero_extend "u")]) |
| (define_code_attr szPat [(sign_extend "") (zero_extend "zero_")]) |
| (define_code_attr szIsa [(sign_extend "x") (zero_extend "z")]) |
| |
| (define_code_iterator sz_xtnd [ sign_extend zero_extend]) |
| (define_code_iterator any_cond [eq ne gt gtu lt ltu ge geu le leu]) |
| (define_code_iterator plusminus [plus minus]) |
| |
| (define_code_attr plusminus_insn [(plus "add") (minus "sub")]) |
| (define_code_attr plusminus_flag [(plus "PLUS") (minus "MINUS")]) |
| (define_code_attr comm [(plus "%") (minus "")]) |
| |
| (define_code_iterator any_logic [and ior xor]) |
| (define_code_attr logic [(and "and") (ior "or") (xor "xor")]) |
| (define_code_attr any_logic_insn [(and "and") (ior "ior") (xor "xor")]) |
| (define_code_attr any_logic_flag [(and "AND") (ior "IOR") (xor "XOR")]) |
| |
| (define_mode_iterator QH [QI HI]) |
| (define_mode_attr qh [(QI "qi") (HI "hi")]) |
| (define_mode_attr QHsz [(QI "2,2,2") (HI "2,2,4")]) |
| (define_mode_attr QHsuffix [(QI "b") (HI "w")]) |
| |
| |
| ;; Function Prologue and Epilogue |
| (define_expand "prologue" |
| [(const_int 0)] |
| "" |
| { |
| cr16_expand_prologue (); |
| DONE; |
| } |
| ) |
| |
| (define_insn "push_for_prologue" |
| [(set (reg:SI SP_REGNUM) |
| (minus:SI (reg:SI SP_REGNUM) |
| (match_operand:SI 0 "immediate_operand" "i")))] |
| "reload_completed" |
| { |
| return cr16_prepare_push_pop_string (0); |
| } |
| [(set_attr "length" "4")] |
| ) |
| |
| (define_expand "epilogue" |
| [(return)] |
| "" |
| { |
| cr16_expand_epilogue (); |
| DONE; |
| } |
| ) |
| |
| (define_insn "pop_and_popret_return" |
| [(set (reg:SI SP_REGNUM) |
| (plus:SI (reg:SI SP_REGNUM) |
| (match_operand:SI 0 "immediate_operand" "i"))) |
| (use (reg:SI RA_REGNUM)) |
| (return)] |
| "reload_completed" |
| { |
| return cr16_prepare_push_pop_string (1); |
| } |
| [(set_attr "length" "4")] |
| ) |
| |
| (define_insn "popret_RA_return" |
| [(use (reg:SI RA_REGNUM)) |
| (return)] |
| "reload_completed" |
| "popret\tra" |
| [(set_attr "length" "2")] |
| ) |
| |
| ;; Arithmetic Instruction Patterns |
| |
| ;; Addition-Subtraction "adddi3/subdi3" insns. |
| (define_insn "<plusminus_insn>di3" |
| [(set (match_operand:DI 0 "register_operand" "=r") |
| (plusminus:DI (match_operand:DI 1 "register_operand" "<comm>0") |
| (match_operand:DI 2 "register_operand" "r")))] |
| "" |
| { |
| return cr16_emit_add_sub_di (operands, <plusminus_flag>); |
| }) |
| |
| (define_insn "addsi3" |
| [(set (match_operand:SI 0 "register_operand" "=r,r,r,r,r") |
| (plus:SI (match_operand:SI 1 "register_operand" "%0,0,0,0,0") |
| (match_operand:SI 2 "reg_si_int_operand" "r,M,N,O,i")))] |
| "" |
| "addd\t%2, %0" |
| [(set_attr "length" "2,2,4,4,6")] |
| ) |
| |
| ;; Addition-Subtraction "addhi3/subhi3" insns. |
| (define_insn "<plusminus_insn>hi3" |
| [(set (match_operand:HI 0 "register_operand" "=c,c,c") |
| (plusminus:HI (match_operand:HI 1 "register_operand" "<comm>0,0,0") |
| (match_operand:HI 2 "reg_hi_int_operand" "c,M,N")))] |
| "" |
| "<plusminus_insn>w\t%2, %0" |
| [(set_attr "length" "2,2,4")] |
| ) |
| |
| ;; Addition-Subtraction "addqi3/subqi3" insns. |
| (define_insn "<plusminus_insn>qi3" |
| [(set (match_operand:QI 0 "register_operand" "=c,c") |
| (plusminus:QI (match_operand:QI 1 "register_operand" "<comm>0,0") |
| (match_operand:QI 2 "reg_qi_int_operand" "c,M")))] |
| "" |
| "<plusminus_insn>b\t%2, %0" |
| [(set_attr "length" "2,2")] |
| ) |
| |
| ;; Subtract Instruction |
| (define_insn "subsi3" |
| [(set (match_operand:SI 0 "register_operand" "=r,r") |
| (minus:SI (match_operand:SI 1 "register_operand" "0,0") |
| (match_operand:SI 2 "reg_si_int_operand" "r,i")))] |
| "" |
| "subd\t%2, %0" |
| [(set_attr "length" "4,6")] |
| ) |
| |
| ;; Multiply and Accumulate Instructions "smachisi3/umachisi3" |
| (define_insn "<sPat>maddhisi4" |
| [(set (match_operand:SI 0 "register_operand" "=r") |
| (plus:SI |
| (mult:SI (sz_xtnd:SI (match_operand:HI 1 "register_operand" "r")) |
| (sz_xtnd:SI (match_operand:HI 2 "register_operand" "r"))) |
| (match_operand:SI 3 "register_operand" "0")))] |
| "TARGET_MAC" |
| "mac<sPat>w\t%1, %2, %0" |
| [(set_attr "length" "2")] |
| ) |
| |
| ;; Multiply Instructions |
| (define_insn "mulhi3" |
| [(set (match_operand:HI 0 "register_operand" "=c,c,c") |
| (mult:HI (match_operand:HI 1 "register_operand" "%0,0,0") |
| (match_operand:HI 2 "reg_or_int_operand" "c,M,N")))] |
| "" |
| "mulw\t%2, %0" |
| [(set_attr "length" "2,2,4")] |
| ) |
| |
| (define_insn "mulqihi3" |
| [(set (match_operand:HI 0 "register_operand" "=c") |
| (mult:HI (sign_extend:HI (match_operand:QI 1 "register_operand" "%0")) |
| (sign_extend:HI (match_operand:QI 2 "register_operand" "c"))))] |
| "" |
| "mulsb\t%2, %0" |
| [(set_attr "length" "2")] |
| ) |
| |
| ;; Bit Set/Clear Instructions |
| (define_expand "insv" |
| [(set (zero_extract (match_operand 0 "memory_operand" "") |
| (match_operand 1 "immediate_operand" "") |
| (match_operand 2 "immediate_operand" "")) |
| (match_operand 3 "immediate_operand" ""))] |
| "TARGET_BIT_OPS" |
| { |
| if (INTVAL (operands[1]) != 1) |
| FAIL; |
| if (INTVAL (operands[2]) < 0 || INTVAL (operands[2]) > 15) |
| FAIL; |
| if (INTVAL (operands[3]) == 1) |
| { |
| if (GET_MODE (operands[0]) == QImode) |
| { |
| emit_insn (gen_set_bitqi (operands[0], operands[2])); |
| DONE; |
| } |
| else if (GET_MODE (operands[0]) == HImode) |
| { |
| emit_insn (gen_set_bithi (operands[0], operands[2])); |
| DONE; |
| } |
| } |
| if (INTVAL (operands[3]) == 0) |
| { |
| if (GET_MODE (operands[0]) == QImode) |
| { |
| emit_insn (gen_clr_bitqi (operands[0], operands[2])); |
| DONE; |
| } |
| else if (GET_MODE (operands[0]) == HImode) |
| { |
| emit_insn (gen_clr_bithi (operands[0], operands[2])); |
| DONE; |
| } |
| } |
| } |
| ) |
| |
| (define_insn "set_bit<mode>" |
| [(set (zero_extract:SHORT (match_operand:SHORT 0 "memory_operand" "+m") |
| (const_int 1) |
| (match_operand 1 "immediate_operand" "i")) |
| (const_int 1))] |
| "TARGET_BIT_OPS" |
| "sbit<tIsa>\t%1,%0" |
| [(set_attr "length" "2")] |
| ) |
| |
| (define_insn "clr_bit<mode>" |
| [(set (zero_extract:SHORT (match_operand:SHORT 0 "memory_operand" "+m") |
| (const_int 1) |
| (match_operand 1 "immediate_operand" "i")) |
| (const_int 0))] |
| "TARGET_BIT_OPS" |
| "cbit<tIsa>\t%1,%0" |
| [(set_attr "length" "2")] |
| ) |
| |
| (define_insn "set_bit<mode>_mem" |
| [(set (match_operand:SHORT 0 "bit_operand" "=m") |
| (ior:SHORT (match_dup 0) |
| (match_operand:SHORT 1 "one_bit_operand" "i")) |
| )] |
| "TARGET_BIT_OPS" |
| "sbit<tIsa>\t$%s1,%0" |
| [(set_attr "length" "2")] |
| ) |
| |
| (define_insn "clear_bit<mode>_mem" |
| [(set (match_operand:SHORT 0 "bit_operand" "=m") |
| (and:SHORT (match_dup 0) |
| (match_operand:SHORT 1 "rev_one_bit_operand" "i")) |
| )] |
| "TARGET_BIT_OPS" |
| "cbit<tIsa>\t$%r1,%0" |
| [(set_attr "length" "2")] |
| ) |
| |
| ;; Logical Instructions - and/ior/xor "anddi3/iordi3/xordi3" |
| (define_insn "<any_logic_insn>di3" |
| [(set (match_operand:DI 0 "register_operand" "=r") |
| (any_logic:DI (match_operand:DI 1 "register_operand" "%0") |
| (match_operand:DI 2 "register_operand" "r")))] |
| "" |
| { |
| return cr16_emit_logical_di (operands, <any_logic_flag>); |
| }) |
| |
| ; Logical and/ior/xor "andsi3/iorsi3/xorsi3" |
| (define_insn "<any_logic_insn>si3" |
| [(set (match_operand:SI 0 "register_operand" "=r,r,r,r") |
| (any_logic:SI (match_operand:SI 1 "register_operand" "%0,0,0,0") |
| (match_operand:SI 2 "reg_si_int_operand" "r,M,N,i")))] |
| "" |
| "<logic>d\t%2, %0" |
| [(set_attr "length" "2,2,4,6")] |
| ) |
| |
| ; Logical and/ior/xor in HImode "andhi3/iorhi3/xorhi3" |
| ; Logical and/ior/xor in QImode "andqi3/iorqi3/xorqi3" |
| (define_insn "<any_logic_insn><qh>3" |
| [(set (match_operand:QH 0 "register_operand" "=c,c,c") |
| (any_logic:QH (match_operand:QH 1 "register_operand" "%0,0,0") |
| (match_operand:QH 2 "reg_hi_int_operand" "c,M,N")))] |
| "" |
| "<logic><QHsuffix>\t%2, %0" |
| [(set_attr "length" "<QHsz>")] |
| ) |
| |
| ;; Sign and Zero Extend Instructions |
| (define_insn "<szPat>extendhisi2" |
| [(set (match_operand:SI 0 "register_operand" "=r") |
| (sz_xtnd:SI (match_operand:HI 1 "register_operand" "r")))] |
| "" |
| "mov<szIsa>w\t%1, %0" |
| [(set_attr "length" "4")] |
| ) |
| |
| (define_insn "<szPat>extendqihi2" |
| [(set (match_operand:HI 0 "register_operand" "=r") |
| (sz_xtnd:HI (match_operand:QI 1 "register_operand" "r")))] |
| "" |
| "mov<szIsa>b\t%1, %0" |
| [(set_attr "length" "4")] |
| ) |
| |
| ;; One's Complement |
| (define_insn "one_cmpldi2" |
| [(set (match_operand:DI 0 "register_operand" "=r") |
| (not:DI (match_operand:DI 1 "register_operand" "0")))] |
| "" |
| { |
| rtx xoperand ; |
| int reg0 = REGNO (operands[0]); |
| |
| xoperand = gen_rtx_REG (SImode, reg0 + 2); |
| output_asm_insn ("xord\t$-1, %0", operands); |
| output_asm_insn ("xord\t$-1, %0", &xoperand); |
| return "" ; |
| } |
| [(set_attr "length" "12")] |
| ) |
| |
| (define_insn "one_cmpl<mode>2" |
| [(set (match_operand:CR16IM 0 "register_operand" "=r") |
| (not:CR16IM (match_operand:CR16IM 1 "register_operand" "0")))] |
| "" |
| "xor<tIsa>\t$-1, %0" |
| [(set_attr "length" "2")] |
| ) |
| |
| ;; Arithmetic Left and Right Shift Instructions |
| (define_insn "ashlqi3" |
| [(set (match_operand:QI 0 "register_operand" "=c,c") |
| (ashift:QI (match_operand:QI 1 "register_operand" "0,0") |
| (match_operand:QI 2 "nonmemory_operand" "c,I")))] |
| "" |
| "ashub\t%2, %0" |
| [(set_attr "length" "2,2")] |
| ) |
| |
| (define_insn "ashlhi3" |
| [(set (match_operand:HI 0 "register_operand" "=c,c") |
| (ashift:HI (match_operand:HI 1 "register_operand" "0,0") |
| (match_operand:QI 2 "nonmemory_operand" "c,J")))] |
| "" |
| "ashuw\t%2, %0" |
| [(set_attr "length" "2,2")] |
| ) |
| |
| (define_insn "ashlsi3" |
| [(set (match_operand:SI 0 "register_operand" "=r,r") |
| (ashift:SI (match_operand:SI 1 "register_operand" "0,0") |
| (match_operand:QI 2 "nonmemory_operand" "r,K")))] |
| "" |
| "ashud\t%2, %0" |
| [(set_attr "length" "2,2")] |
| ) |
| |
| (define_expand "ashr<mode>3" |
| [(set (match_operand:CR16IM 0 "register_operand" "") |
| (ashiftrt:CR16IM (match_operand:CR16IM 1 "register_operand" "") |
| (match_operand:QI 2 "nonmemory_operand" "")))] |
| "" |
| { |
| if (GET_CODE (operands[2]) == CONST_INT) |
| { |
| /* If the constant is not in range, try placing it in a reg */ |
| if (!UNSIGNED_INT_FITS_N_BITS(INTVAL (operands[2]),<shImmBits>)) |
| operands[2] = copy_to_mode_reg(QImode, operands[2]); |
| } |
| |
| if (GET_CODE (operands[2]) != CONST_INT) |
| operands[2] = gen_rtx_NEG (QImode, negate_rtx (QImode, operands[2])); |
| } |
| ) |
| |
| (define_insn "ashrqi3_imm_insn" |
| [(set (match_operand:QI 0 "register_operand" "=c") |
| (ashiftrt:QI (match_operand:QI 1 "register_operand" "0") |
| (match_operand:QI 2 "shift_qi_imm_operand" "i")))] |
| "" |
| "ashub\t$%n2, %0" |
| [(set_attr "length" "2")] |
| ) |
| |
| (define_insn "ashrhi3_imm_insn" |
| [(set (match_operand:HI 0 "register_operand" "=c") |
| (ashiftrt:HI (match_operand:HI 1 "register_operand" "0") |
| (match_operand:QI 2 "shift_hi_imm_operand" "i")))] |
| "" |
| "ashuw\t$%n2, %0" |
| [(set_attr "length" "2")] |
| ) |
| |
| (define_insn "ashrsi3_imm_insn" |
| [(set (match_operand:SI 0 "register_operand" "=r") |
| (ashiftrt:SI (match_operand:SI 1 "register_operand" "0") |
| (match_operand:QI 2 "shift_si_imm_operand" "i")))] |
| "" |
| "ashud\t$%n2, %0" |
| [(set_attr "length" "2")] |
| ) |
| |
| (define_insn "ashrqi3_neg_insn" |
| [(set (match_operand:QI 0 "register_operand" "=c") |
| (ashiftrt:QI (match_operand:QI 1 "register_operand" "0") |
| (neg:QI (match_operand:QI 2 "register_operand" "c"))))] |
| "" |
| "ashub\t%2,%0" |
| [(set_attr "length" "2")] |
| ) |
| |
| (define_insn "ashrhi3_neg_insn" |
| [(set (match_operand:HI 0 "register_operand" "=c") |
| (ashiftrt:HI (match_operand:HI 1 "register_operand" "0") |
| (neg:QI (match_operand:QI 2 "register_operand" "c"))))] |
| "" |
| "ashuw\t%2,%0" |
| [(set_attr "length" "2")] |
| ) |
| |
| (define_insn "ashrdi3_neg_insn" |
| [(set (match_operand:SI 0 "register_operand" "=r") |
| (ashiftrt:SI (match_operand:SI 1 "register_operand" "0") |
| (neg:QI (match_operand:QI 2 "register_operand" "r"))))] |
| "" |
| "ashud\t%2,%0" |
| [(set_attr "length" "2")] |
| ) |
| |
| (define_expand "lshr<mode>3" |
| [(set (match_operand:CR16IM 0 "register_operand" "") |
| (lshiftrt:CR16IM (match_operand:CR16IM 1 "register_operand" "") |
| (match_operand:QI 2 "reg_or_int_operand" "")))] |
| "" |
| { |
| if (GET_CODE (operands[2]) == CONST_INT) |
| { |
| /* If the constant is not in range, try placing it in a reg */ |
| if (!UNSIGNED_INT_FITS_N_BITS(INTVAL (operands[2]),<shImmBits>)) |
| operands[2] = copy_to_mode_reg(QImode, operands[2]); |
| } |
| |
| if (GET_CODE (operands[2]) != CONST_INT) |
| operands[2] = gen_rtx_NEG (QImode, negate_rtx (QImode, operands[2])); |
| } |
| ) |
| |
| (define_insn "lshrqi3_imm_insn" |
| [(set (match_operand:QI 0 "register_operand" "=c") |
| (lshiftrt:QI (match_operand:QI 1 "register_operand" "0") |
| (match_operand:QI 2 "shift_qi_operand" "Q")))] |
| "" |
| "lshb\t$%n2, %0" |
| [(set_attr "length" "2")] |
| ) |
| |
| (define_insn "lshrhi3_imm_insn" |
| [(set (match_operand:HI 0 "register_operand" "=c") |
| (lshiftrt:HI (match_operand:HI 1 "register_operand" "0") |
| (match_operand:QI 2 "shift_hi_operand" "R")))] |
| "" |
| "lshw\t$%n2, %0" |
| [(set_attr "length" "2")] |
| ) |
| |
| (define_insn "lshrsi3_imm_insn" |
| [(set (match_operand:SI 0 "register_operand" "=r") |
| (lshiftrt:SI (match_operand:SI 1 "register_operand" "0") |
| (match_operand:QI 2 "shift_si_operand" "S")))] |
| "" |
| "lshd\t$%n2, %0" |
| [(set_attr "length" "2")] |
| ) |
| |
| (define_insn "lshrqi3_neg_insn" |
| [(set (match_operand:QI 0 "register_operand" "=c") |
| (lshiftrt:QI (match_operand:QI 1 "register_operand" "0") |
| (neg:QI (match_operand:QI 2 "register_operand" "c"))))] |
| "" |
| "lshb\t%2,%0" |
| [(set_attr "length" "2")] |
| ) |
| |
| (define_insn "lshrhi3_neg_insn" |
| [(set (match_operand:HI 0 "register_operand" "=c") |
| (lshiftrt:HI (match_operand:HI 1 "register_operand" "0") |
| (neg:QI (match_operand:QI 2 "register_operand" "c"))))] |
| "" |
| "lshw\t%2,%0" |
| [(set_attr "length" "2")] |
| ) |
| |
| (define_insn "lshrsi3_neg_insn" |
| [(set (match_operand:SI 0 "register_operand" "=r") |
| (lshiftrt:SI (match_operand:SI 1 "register_operand" "0") |
| (neg:QI (match_operand:QI 2 "register_operand" "r"))))] |
| "" |
| "lshd\t%2,%0" |
| [(set_attr "length" "2")] |
| ) |
| |
| ;; Move Instructions |
| |
| ;; Move any non-immediate operand 0 to a general operand 1. |
| ;; This applies only before starting the reload process |
| ;; Operand 0 is not a register operand of type mode MODE |
| ;; If Operand 0 is a push operand of type mode MODE |
| ;; then, if Operand 1 is a non-SP register |
| ;; then, Operand 1 = copy_to_mode_reg (<MODE>mode, Operand 1) |
| ;; endif |
| ;; else |
| ;; if Operand 1 is either register or 4-bit immediate constant |
| ;; then, Operand 1 = copy_to_mode_reg (<MODE>mode, Operand 1) |
| ;; endif |
| ;; endif |
| ;; |
| ;; What does copy_to_mode_reg (mode, rtx val) do? |
| ;; Copy the value into new temp reg and return the reg where the |
| ;; mode of the new reg is always mode MODE when value is constant |
| ;; |
| ;; Why should copy_to_mode_reg be called? |
| ;; All sorts of move are nor supported by CR16. Therefore, |
| ;; when unsupported move is encountered, the additional instructions |
| ;; will be introduced for the purpose. |
| ;; |
| ;; A new move insn is inserted for Op 1 when one of the following |
| ;; conditions is met. |
| ;; Case 1: Op 0 is push_operand |
| ;; Op 1 is SP register |
| ;; |
| ;; Case 2: Op 0 is not push_operand |
| ;; Op 1 is neither register nor unsigned 4-bit immediate |
| |
| (define_expand "mov<mode>" |
| [(set (match_operand:ALLMTD 0 "nonimmediate_operand" "") |
| (match_operand:ALLMTD 1 "general_operand" ""))] |
| "" |
| { |
| if (!(reload_in_progress || reload_completed)) |
| { |
| /* Only if Op0 is a register operand. */ |
| if (!register_operand (operands[0], <MODE>mode)) |
| { |
| if (push_operand (operands[0], <MODE>mode)) |
| { |
| /* Use copy_to_mode_reg only if the register needs |
| to be pushed is SP as CR16 does not support pushing SP. */ |
| if (!nosp_reg_operand (operands[1], <MODE>mode)) |
| operands[1] = copy_to_mode_reg (<MODE>mode, operands[1]); |
| } |
| else |
| { |
| /* Use copy_to_mode_reg if op1 is not register operand |
| subject to conditions inside. */ |
| if (!register_operand (operands[1], <MODE>mode)) |
| { |
| /* CR16 does not support moving immediate to SI or SF |
| type memory. */ |
| if (<MODE>mode == SImode || <MODE>mode == SFmode || |
| <MODE>mode == DImode || <MODE>mode == DFmode) |
| operands[1] = copy_to_mode_reg (<MODE>mode, operands[1]); |
| else |
| /* moving imm4 is supported by CR16 instruction. */ |
| if (!u4bits_operand (operands[1], <MODE>mode)) |
| operands[1] = copy_to_mode_reg (<MODE>mode, operands[1]); |
| } |
| } |
| } |
| |
| /* If operand-1 is a symbol, convert it into a BRO or GOT Format. */ |
| if (flag_pic && ! legitimate_pic_operand_p (operands[1])) |
| { |
| operands[1] = legitimize_pic_address (operands[1], <MODE>mode, 0); |
| } |
| } |
| } |
| ) |
| |
| ; ALLMT : QI,HI,SI,SF |
| ; pushCnstr : Push constraints |
| ; QI : X |
| ; HI,SI,SF,DI,DF : < |
| ; b : All non-sp registers |
| ; tpush : Push count |
| ; QI,HI : 1 |
| ; SI,SF : 2 |
| ; DI,DF : 4 |
| (define_insn "push<mode>_internal" |
| [(set (match_operand:ALLMTD 0 "push_operand" "=<pushCnstr>") |
| (match_operand:ALLMTD 1 "nosp_reg_operand" "b"))] |
| "" |
| "push\t$<tpush>,%p1" |
| [(set_attr "length" "2")] |
| ) |
| |
| ; (DI, DF) move |
| (define_insn "*mov<mode>_double" |
| [(set (match_operand:DOUBLE 0 "nonimmediate_operand" "=r, r, r, m") |
| (match_operand:DOUBLE 1 "general_operand" "r, <iFD>, m, r"))] |
| "register_operand (operands[0], DImode) |
| || register_operand (operands[0], DFmode) |
| || register_operand (operands[1], DImode) |
| || register_operand (operands[1], DFmode)" |
| { |
| if (which_alternative == 0) { |
| rtx xoperands[2]; |
| int reg0 = REGNO (operands[0]); |
| int reg1 = REGNO (operands[1]); |
| |
| xoperands[0] = gen_rtx_REG (SImode, reg0 + 2); |
| xoperands[1] = gen_rtx_REG (SImode, reg1 + 2); |
| if ((reg1 + 2) != reg0) |
| { |
| output_asm_insn ("movd\t%1, %0", operands); |
| output_asm_insn ("movd\t%1, %0", xoperands); |
| } |
| else |
| { |
| output_asm_insn ("movd\t%1, %0", xoperands); |
| output_asm_insn ("movd\t%1, %0", operands); |
| }} |
| |
| else if (which_alternative == 1) { |
| rtx lo_operands[2]; |
| rtx hi_operands[2]; |
| |
| lo_operands[0] = gen_rtx_REG (SImode, REGNO (operands[0])); |
| hi_operands[0] = gen_rtx_REG (SImode, REGNO (operands[0]) + 2); |
| lo_operands[1] = simplify_gen_subreg (SImode, operands[1], |
| VOIDmode == GET_MODE (operands[1]) |
| ? DImode : GET_MODE (operands[1]), 0); |
| hi_operands[1] = simplify_gen_subreg (SImode, operands[1], |
| VOIDmode == GET_MODE (operands[1]) |
| ? DImode : GET_MODE (operands[1]), 4); |
| output_asm_insn ("movd\t%1, %0", lo_operands); |
| output_asm_insn ("movd\t%1, %0", hi_operands);} |
| |
| else if (which_alternative == 2) { |
| rtx xoperands[2]; |
| int reg0 = REGNO (operands[0]), reg1 = -2; |
| rtx addr; |
| |
| if (MEM_P (operands[1])) |
| addr = XEXP (operands[1], 0); |
| else |
| addr = NULL_RTX; |
| switch (GET_CODE (addr)) |
| { |
| case REG: |
| case SUBREG: |
| reg1 = REGNO (addr); |
| break; |
| case PLUS: |
| switch (GET_CODE (XEXP (addr, 0))) { |
| case REG: |
| case SUBREG: |
| reg1 = REGNO (XEXP (addr, 0)); |
| break; |
| case PLUS: |
| reg1 = REGNO (XEXP (XEXP (addr, 0), 0)); |
| break; |
| default: |
| inform (DECL_SOURCE_LOCATION (cfun->decl), "unexpected expression; addr:"); |
| debug_rtx (addr); |
| inform (DECL_SOURCE_LOCATION (cfun->decl), "operands[1]:"); |
| debug_rtx (operands[1]); |
| inform (DECL_SOURCE_LOCATION (cfun->decl), "generated code might now work\n"); |
| break;} |
| break; |
| default: |
| break; |
| } |
| |
| xoperands[0] = gen_rtx_REG (SImode, reg0 + 2); |
| xoperands[1] = offset_address (operands[1], GEN_INT (4), 2); |
| gcc_assert ((reg0 + 1) != reg1); |
| if (reg0 != reg1 && (reg1 + 1) != reg0) |
| { |
| output_asm_insn ("loadd\t%1, %0", operands); |
| output_asm_insn ("loadd\t%1, %0", xoperands); |
| } |
| else |
| { |
| output_asm_insn ("loadd\t%1, %0", xoperands); |
| output_asm_insn ("loadd\t%1, %0", operands); |
| }} |
| else |
| { |
| rtx xoperands[2]; |
| xoperands[0] = offset_address (operands[0], GEN_INT (4), 2); |
| xoperands[1] = gen_rtx_REG (SImode, REGNO (operands[1]) + 2); |
| output_asm_insn ("stord\t%1, %0", operands); |
| output_asm_insn ("stord\t%1, %0", xoperands); |
| } |
| return ""; |
| } |
| [(set_attr "length" "4, <lImmArithD>, <lImmArithD>, <lImmArithD>")] |
| ) |
| |
| ; All long (SI, SF) register move, load and store operations |
| ; The print_operand will take care of printing the register pair |
| ; when mode is SI/SF and register is in SHORT_REGS |
| (define_insn "*mov<mode>_long" |
| [(set (match_operand:LONG 0 "nonimmediate_operand" "=r, r, r, m") |
| (match_operand:LONG 1 "general_operand" "r, <iF>, m, r"))] |
| "register_operand (operands[0], <MODE>mode) |
| || register_operand (operands[1], <MODE>mode)" |
| "@ |
| mov<tIsa>\t%1, %0 |
| mov<tIsa>\t%1, %0 |
| load<tIsa>\t%1, %0 |
| stor<tIsa>\t%1, %0" |
| [(set_attr "length" "2,<lImmArith>,<lImmArith>,<lImmArith>")] |
| ) |
| |
| ;; All short (QI, HI) register move, load and store operations |
| (define_insn "*mov<mode>_short" |
| [(set (match_operand:SHORT 0 "nonimmediate_operand" "=r, r, r, m, m") |
| (match_operand:SHORT 1 "general_operand" "r, <iF>, m, r, <LL>"))] |
| "(register_operand (operands[0], <MODE>mode)) |
| || (store_operand (operands[0], <MODE>mode) |
| && (register_operand (operands[1], <MODE>mode) |
| || u4bits_operand (operands[1], <MODE>mode)))" |
| "@ |
| mov<tIsa>\t%1, %0 |
| mov<tIsa>\t%1, %0 |
| load<tIsa>\t%1, %0 |
| stor<tIsa>\t%1, %0 |
| stor<tIsa>\t%1, %0" |
| [(set_attr "length" "2,<lImmArith>,<lImmArith>,<lImmArith>,<lImmArith>")] |
| ) |
| |
| ;; Compare Instructions |
| ; Instruction generated compares the operands in reverse order |
| ; Therefore, while printing the asm, the reverse of the |
| ; compare condition shall be printed. |
| (define_insn "cbranch<mode>4" |
| [(set (pc) |
| (if_then_else (match_operator 0 "ordered_comparison_operator" |
| [(match_operand:CR16IM 1 "register_operand" "r,r") |
| (match_operand:CR16IM 2 "nonmemory_operand" "r,n")]) |
| (label_ref (match_operand 3 "" "")) |
| (pc))) |
| (clobber (cc0))] |
| "" |
| "cmp<tIsa>\t%2, %1\;b%d0\t%l3" |
| [(set_attr "length" "6,6")] |
| ) |
| |
| (define_expand "cmp<mode>" |
| [(parallel [(set (cc0) |
| (compare (match_operand:CR16IM 0 "register_operand" "") |
| (match_operand:CR16IM 1 "nonmemory_operand" ""))) |
| (clobber (match_scratch:HI 2 "=r"))] ) ] |
| "" |
| "") |
| |
| ;; Scond Instructions |
| (define_expand "cstore<mode>4" |
| [(set (cc0) |
| (compare (match_operand:CR16IM 2 "register_operand" "") |
| (match_operand:CR16IM 3 "nonmemory_operand" ""))) |
| (set (match_operand:HI 0 "register_operand") |
| (match_operator:HI 1 "ordered_comparison_operator" |
| [(cc0) (const_int 0)]))] |
| "" |
| "" |
| ) |
| |
| (define_insn "*cmp<mode>_insn" |
| [(set (cc0) |
| (compare (match_operand:CR16IM 0 "register_operand" "r,r") |
| (match_operand:CR16IM 1 "nonmemory_operand" "r,n")))] |
| "" |
| "cmp<tIsa>\t%1, %0" |
| [(set_attr "length" "2,4")] |
| ) |
| |
| (define_insn "sCOND_internal" |
| [(set (match_operand:HI 0 "register_operand" "=r") |
| (match_operator:HI 1 "ordered_comparison_operator" |
| [(cc0) (const_int 0)]))] |
| "" |
| "s%d1\t%0" |
| [(set_attr "length" "2")] |
| ) |
| |
| ;; Jumps and Branches |
| (define_insn "indirect_jump_return" |
| [(set (pc) |
| (reg:SI RA_REGNUM)) |
| (return)] |
| "reload_completed" |
| "jump\t (ra)" |
| [(set_attr "length" "2")] |
| ) |
| |
| (define_insn "jump_return" |
| [(unspec:SI [(const_int 0)] UNSPEC_RETURN_ADDR) |
| (return)] |
| "reload_completed" |
| "jump\t(ra)" |
| [(set_attr "length" "2")] |
| ) |
| |
| (define_insn "indirect_jump" |
| [(set (pc) |
| (match_operand:SI 0 "reg_or_sym_operand" "r,i"))] |
| "" |
| "@ |
| jump\t%0 |
| br\t%a0" |
| [(set_attr "length" "2,6")] |
| ) |
| |
| (define_insn "interrupt_return" |
| [(unspec_volatile [(const_int 0)] 0) |
| (return)] |
| "" |
| { |
| return cr16_prepare_push_pop_string (1); |
| } |
| [(set_attr "length" "14")] |
| ) |
| |
| (define_insn "jump_to_imm" |
| [(set (pc) |
| (match_operand 0 "jump_imm_operand" "i"))] |
| "" |
| "br\t%c0" |
| [(set_attr "length" "6")] |
| ) |
| |
| (define_insn "jump" |
| [(set (pc) |
| (label_ref (match_operand 0 "" "")))] |
| "" |
| "br\t%l0" |
| [(set_attr "length" "6")] |
| ) |
| |
| ;; Table Jump |
| (define_insn "tablejump" |
| [(set (pc) |
| (match_operand:SI 0 "register_operand" "r")) |
| (use (label_ref:SI (match_operand 1 "" "")))] |
| "!flag_pic" |
| "jump\t%0" |
| [(set_attr "length" "2")] |
| ) |
| |
| ;; Call Instructions |
| (define_expand "call" |
| [(call (match_operand:QI 0 "memory_operand" "") |
| (match_operand 1 "" ""))] |
| "" |
| { |
| if (flag_pic && ! legitimate_pic_operand_p (operands[0])) |
| { |
| operands[0] = gen_const_mem (QImode, |
| legitimize_pic_address (XEXP (operands[0], 0), Pmode, 0)); |
| emit_call_insn (gen_cr16_call (operands[0], operands[1])); |
| } |
| else |
| emit_call_insn (gen_cr16_call (operands[0], operands[1])); |
| DONE; |
| } |
| ) |
| |
| (define_expand "cr16_call" |
| [(parallel |
| [(call (match_operand:QI 0 "memory_operand" "") |
| (match_operand 1 "" "")) |
| (clobber (reg:SI RA_REGNUM))])] |
| "" |
| "" |
| ) |
| |
| (define_insn "cr16_call_insn_branch_pic" |
| [(call (mem:QI (match_operand:SI 0 "call_imm_operand" "i")) |
| (match_operand 1 "" "")) |
| (clobber (match_operand:SI 2 "register_operand" "+r"))] |
| "flag_pic == FAR_PIC" |
| { |
| if (GET_CODE (operands[0]) != CONST_INT) |
| return "loadd\t%g0, %2 \n\tjal %2"; |
| else |
| return "jal %2"; |
| } |
| [(set_attr "length" "8")] |
| ) |
| |
| (define_insn "cr16_call_insn_branch" |
| [(call (mem:QI (match_operand:SI 0 "call_imm_operand" "i")) |
| (match_operand 1 "" "")) |
| (clobber (match_operand:SI 2 "register_operand" "+r"))] |
| "flag_pic == 0 || flag_pic == NEAR_PIC" |
| { |
| /* Print the immediate address for bal |
| 'b' is used instead of 'a' to avoid compiler calling |
| the GO_IF_LEGITIMATE_ADDRESS which cannot |
| perform checks on const_int code addresses as it |
| assumes all const_int are data addresses. |
| */ |
| if (GET_CODE (operands[0]) != CONST_INT) |
| return "bal (ra), %a0"; |
| else |
| operands[4] = GEN_INT ((INTVAL (operands[0]))>>1); |
| return "movd\t%g4,\t(r1,r0)\n\tjal\t(r1,r0)"; |
| } |
| [(set_attr "length" "6")] |
| ) |
| |
| (define_insn "cr16_call_insn_jump" |
| [(call (mem:QI (match_operand:SI 0 "register_operand" "r")) |
| (match_operand 1 "" "")) |
| (clobber (match_operand:SI 2 "register_operand" "+r"))] |
| "" |
| "jal\t%0" |
| [(set_attr "length" "2")] |
| ) |
| |
| ;; Call Value Instructions |
| |
| (define_expand "call_value" |
| [(set (match_operand 0 "general_operand" "") |
| (call (match_operand:QI 1 "memory_operand" "") |
| (match_operand 2 "" "")))] |
| "" |
| { |
| if (flag_pic && !legitimate_pic_operand_p (operands[1])) |
| { |
| operands[1] = gen_const_mem (QImode, |
| legitimize_pic_address (XEXP (operands[1], 0), Pmode, 0)); |
| emit_call_insn (gen_cr16_call_value (operands[0], operands[1], operands[2])); |
| } |
| else |
| emit_call_insn (gen_cr16_call_value (operands[0], operands[1], operands[2])); |
| DONE; |
| } |
| ) |
| |
| (define_expand "cr16_call_value" |
| [(parallel |
| [(set (match_operand 0 "general_operand" "") |
| (call (match_operand 1 "memory_operand" "") |
| (match_operand 2 "" ""))) |
| (clobber (reg:SI RA_REGNUM))])] |
| "" |
| "" |
| ) |
| |
| (define_insn "cr16_call_value_insn_branch_pic" |
| [(set (match_operand 0 "" "=g") |
| (call (mem:QI (match_operand:SI 1 "call_imm_operand" "i")) |
| (match_operand 2 "" ""))) |
| (clobber (match_operand:SI 3 "register_operand" "+r"))] |
| "flag_pic == FAR_PIC" |
| { |
| if (GET_CODE (operands[1]) != CONST_INT) |
| return "loadd\t%g1, %3 \n\tjal %3"; |
| else |
| return "jal %3"; |
| } |
| [(set_attr "length" "8")] |
| ) |
| |
| (define_insn "cr16_call_value_insn_branch" |
| [(set (match_operand 0 "" "=g") |
| (call (mem:QI (match_operand:SI 1 "call_imm_operand" "i")) |
| (match_operand 2 "" ""))) |
| (clobber (match_operand:SI 3 "register_operand" "+r"))] |
| "flag_pic == 0 || flag_pic == NEAR_PIC" |
| { |
| /* Print the immediate address for bal |
| 'b' is used instead of 'a' to avoid compiler calling |
| the GO_IF_LEGITIMATE_ADDRESS which cannot |
| perform checks on const_int code addresses as it |
| assumes all const_int are data addresses. |
| */ |
| if (GET_CODE (operands[1]) != CONST_INT) |
| return "bal (ra), %a1"; |
| else |
| { |
| operands[4] = GEN_INT ((INTVAL (operands[1]))>>1); |
| return "movd\t%g4,\t(r1,r0)\n\tjal\t(r1,r0)"; |
| } |
| } |
| [(set_attr "length" "6")] |
| ) |
| |
| |
| (define_insn "cr16_call_value_insn_jump" |
| [(set (match_operand 0 "" "=g") |
| (call (mem:QI (match_operand:SI 1 "register_operand" "r")) |
| (match_operand 2 "" ""))) |
| (clobber (match_operand:SI 3 "register_operand" "+r"))] |
| "" |
| "jal\t%1" |
| [(set_attr "length" "2")] |
| ) |
| |
| |
| ;; Nop |
| (define_insn "nop" |
| [(const_int 0)] |
| "" |
| "nop\t" |
| ) |
| |
| ;; PIC |
| /* When generating pic, we need to load the symbol offset into a register. |
| So that the optimizer does not confuse this with a normal symbol load |
| we use an unspec. The offset will be loaded from a constant pool entry, |
| since that is the only type of relocation we can use. */ |
| |
| (define_insn "unspec_bro_addr" |
| [(set (match_operand:SI 0 "register_operand" "=r") |
| (unspec:SI [(match_operand 1 "" "")] UNSPEC_PIC_ADDR))] |
| "" |
| "movd \t%f1, %0" |
| [(set_attr "length" "4")] |
| ) |
| |
| (define_insn "unspec_got_addr" |
| [(set (match_operand:SI 0 "register_operand" "=r") |
| (unspec:SI [(match_operand 1 "" "")] UNSPEC_PIC_LOAD_ADDR))] |
| "" |
| "loadd \t%g1, %0" |
| [(set_attr "length" "6")] |
| ) |