Mercurial > hg > CbC > CbC_gcc
diff gcc/config/nds32/nds32-utils.c @ 131:84e7813d76e9
gcc-8.2
author | mir3636 |
---|---|
date | Thu, 25 Oct 2018 07:37:49 +0900 |
parents | |
children | 1830386684a0 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gcc/config/nds32/nds32-utils.c Thu Oct 25 07:37:49 2018 +0900 @@ -0,0 +1,634 @@ +/* Auxiliary functions for pipeline descriptions pattern of Andes + NDS32 cpu for GNU compiler + Copyright (C) 2012-2018 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 + <http://www.gnu.org/licenses/>. */ + +/* ------------------------------------------------------------------------ */ + +#define IN_TARGET_CODE 1 + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "backend.h" +#include "target.h" +#include "rtl.h" +#include "tree.h" +#include "memmodel.h" +#include "tm_p.h" +#include "optabs.h" /* For GEN_FCN. */ +#include "recog.h" +#include "tm-constrs.h" +#include "insn-attr.h" + + +namespace nds32 { + +/* Get the rtx in the PATTERN field of an insn. If INSN is not an insn, + the funciton doesn't change anything and returns it directly. */ +rtx +extract_pattern_from_insn (rtx insn) +{ + if (INSN_P (insn)) + return PATTERN (insn); + + return insn; +} + +/* Get the number of elements in a parallel rtx. */ +size_t +parallel_elements (rtx parallel_rtx) +{ + parallel_rtx = extract_pattern_from_insn (parallel_rtx); + gcc_assert (GET_CODE (parallel_rtx) == PARALLEL); + + return XVECLEN (parallel_rtx, 0); +} + +/* Extract an rtx from a parallel rtx with index NTH. If NTH is a negative + value, the function returns the last NTH rtx. */ +rtx +parallel_element (rtx parallel_rtx, int nth) +{ + parallel_rtx = extract_pattern_from_insn (parallel_rtx); + gcc_assert (GET_CODE (parallel_rtx) == PARALLEL); + + int len = parallel_elements (parallel_rtx); + + if (nth >= 0) + { + if (nth >= len) + return NULL_RTX; + + return XVECEXP (parallel_rtx, 0, nth); + } + else + { + if (len + nth < 0) + return NULL_RTX; + + return XVECEXP (parallel_rtx, 0, len + nth); + } +} + +/* Functions to determine whether INSN is single-word, double-word + or partial-word load/store insn. */ + +bool +load_single_p (rtx_insn *insn) +{ + if (get_attr_type (insn) != TYPE_LOAD) + return false; + + if (INSN_CODE (insn) == CODE_FOR_move_di || + INSN_CODE (insn) == CODE_FOR_move_df) + return false; + + return true; +} + +bool +store_single_p (rtx_insn *insn) +{ + if (get_attr_type (insn) != TYPE_STORE) + return false; + + if (INSN_CODE (insn) == CODE_FOR_move_di || + INSN_CODE (insn) == CODE_FOR_move_df) + return false; + + return true; +} + +bool +load_double_p (rtx_insn *insn) +{ + if (get_attr_type (insn) != TYPE_LOAD) + return false; + + if (INSN_CODE (insn) != CODE_FOR_move_di && + INSN_CODE (insn) != CODE_FOR_move_df) + return false; + + return true; +} + +bool +store_double_p (rtx_insn *insn) +{ + if (get_attr_type (insn) != TYPE_STORE) + return false; + + if (INSN_CODE (insn) != CODE_FOR_move_di && + INSN_CODE (insn) != CODE_FOR_move_df) + return false; + + return true; +} + +bool +store_offset_reg_p (rtx_insn *insn) +{ + if (get_attr_type (insn) != TYPE_STORE) + return false; + + rtx offset_rtx = extract_offset_rtx (insn); + + if (offset_rtx == NULL_RTX) + return false; + + if (REG_P (offset_rtx)) + return true; + + return false; +} + +/* Determine if INSN is a post update insn. */ +bool +post_update_insn_p (rtx_insn *insn) +{ + if (find_post_update_rtx (insn) == -1) + return false; + else + return true; +} + +/* Check if the address of MEM_RTX consists of a base register and an + immediate offset. */ +bool +immed_offset_p (rtx mem_rtx) +{ + gcc_assert (MEM_P (mem_rtx)); + + rtx addr_rtx = XEXP (mem_rtx, 0); + + /* (mem (reg)) is equivalent to (mem (plus (reg) (const_int 0))) */ + if (REG_P (addr_rtx)) + return true; + + /* (mem (plus (reg) (const_int))) */ + if (GET_CODE (addr_rtx) == PLUS + && GET_CODE (XEXP (addr_rtx, 1)) == CONST_INT) + return true; + + return false; +} + +/* Find the post update rtx in INSN. If INSN is a load/store multiple insn, + the function returns the vector index of its parallel part. If INSN is a + single load/store insn, the function returns 0. If INSN is not a post- + update insn, the function returns -1. */ +int +find_post_update_rtx (rtx_insn *insn) +{ + rtx mem_rtx; + int i, len; + + switch (get_attr_type (insn)) + { + case TYPE_LOAD_MULTIPLE: + case TYPE_STORE_MULTIPLE: + /* Find a pattern in a parallel rtx: + (set (reg) (plus (reg) (const_int))) */ + len = parallel_elements (insn); + for (i = 0; i < len; ++i) + { + rtx curr_insn = parallel_element (insn, i); + + if (GET_CODE (curr_insn) == SET + && REG_P (SET_DEST (curr_insn)) + && GET_CODE (SET_SRC (curr_insn)) == PLUS) + return i; + } + return -1; + + case TYPE_LOAD: + case TYPE_FLOAD: + case TYPE_STORE: + case TYPE_FSTORE: + mem_rtx = extract_mem_rtx (insn); + /* (mem (post_inc (reg))) */ + switch (GET_CODE (XEXP (mem_rtx, 0))) + { + case POST_INC: + case POST_DEC: + case POST_MODIFY: + return 0; + + default: + return -1; + } + + default: + gcc_unreachable (); + } +} + +/* Extract the MEM rtx from a load/store insn. */ +rtx +extract_mem_rtx (rtx_insn *insn) +{ + rtx body = PATTERN (insn); + + switch (get_attr_type (insn)) + { + case TYPE_LOAD: + case TYPE_FLOAD: + if (MEM_P (SET_SRC (body))) + return SET_SRC (body); + + /* unaligned address: (unspec [(mem)]) */ + if (GET_CODE (SET_SRC (body)) == UNSPEC) + { + gcc_assert (MEM_P (XVECEXP (SET_SRC (body), 0, 0))); + return XVECEXP (SET_SRC (body), 0, 0); + } + + /* (sign_extend (mem)) */ + gcc_assert (MEM_P (XEXP (SET_SRC (body), 0))); + return XEXP (SET_SRC (body), 0); + + case TYPE_STORE: + case TYPE_FSTORE: + if (MEM_P (SET_DEST (body))) + return SET_DEST (body); + + /* unaligned address: (unspec [(mem)]) */ + if (GET_CODE (SET_DEST (body)) == UNSPEC) + { + gcc_assert (MEM_P (XVECEXP (SET_DEST (body), 0, 0))); + return XVECEXP (SET_DEST (body), 0, 0); + } + + /* (sign_extend (mem)) */ + gcc_assert (MEM_P (XEXP (SET_DEST (body), 0))); + return XEXP (SET_DEST (body), 0); + + default: + gcc_unreachable (); + } +} + +/* Extract the base register from load/store insns. The function returns + NULL_RTX if the address is not consist of any registers. */ +rtx +extract_base_reg (rtx_insn *insn) +{ + int post_update_rtx_index; + rtx mem_rtx; + rtx plus_rtx; + + /* Find the MEM rtx. If we can find an insn updating the base register, + the base register will be returned directly. */ + switch (get_attr_type (insn)) + { + case TYPE_LOAD_MULTIPLE: + post_update_rtx_index = find_post_update_rtx (insn); + + if (post_update_rtx_index != -1) + return SET_DEST (parallel_element (insn, post_update_rtx_index)); + + mem_rtx = SET_SRC (parallel_element (insn, 0)); + break; + + case TYPE_STORE_MULTIPLE: + post_update_rtx_index = find_post_update_rtx (insn); + + if (post_update_rtx_index != -1) + return SET_DEST (parallel_element (insn, post_update_rtx_index)); + + mem_rtx = SET_DEST (parallel_element (insn, 0)); + break; + + case TYPE_LOAD: + case TYPE_FLOAD: + case TYPE_STORE: + case TYPE_FSTORE: + mem_rtx = extract_mem_rtx (insn); + break; + + default: + gcc_unreachable (); + } + + gcc_assert (MEM_P (mem_rtx)); + + /* (mem (reg)) */ + if (REG_P (XEXP (mem_rtx, 0))) + return XEXP (mem_rtx, 0); + + /* (mem (lo_sum (reg) (symbol_ref)) */ + if (GET_CODE (XEXP (mem_rtx, 0)) == LO_SUM) + return XEXP (XEXP (mem_rtx, 0), 0); + + plus_rtx = XEXP (mem_rtx, 0); + + if (GET_CODE (plus_rtx) == SYMBOL_REF + || GET_CODE (plus_rtx) == CONST) + return NULL_RTX; + + /* (mem (plus (reg) (const_int))) or + (mem (plus (mult (reg) (const_int 4)) (reg))) or + (mem (post_inc (reg))) or + (mem (post_dec (reg))) or + (mem (post_modify (reg) (plus (reg) (reg)))) */ + gcc_assert (GET_CODE (plus_rtx) == PLUS + || GET_CODE (plus_rtx) == POST_INC + || GET_CODE (plus_rtx) == POST_DEC + || GET_CODE (plus_rtx) == POST_MODIFY); + + if (REG_P (XEXP (plus_rtx, 0))) + return XEXP (plus_rtx, 0); + + gcc_assert (REG_P (XEXP (plus_rtx, 1))); + return XEXP (plus_rtx, 1); +} + +/* Extract the offset rtx from load/store insns. The function returns + NULL_RTX if offset is absent. */ +rtx +extract_offset_rtx (rtx_insn *insn) +{ + rtx mem_rtx; + rtx plus_rtx; + rtx offset_rtx; + + /* Find the MEM rtx. The multiple load/store insns doens't have + the offset field so we can return NULL_RTX here. */ + switch (get_attr_type (insn)) + { + case TYPE_LOAD_MULTIPLE: + case TYPE_STORE_MULTIPLE: + return NULL_RTX; + + case TYPE_LOAD: + case TYPE_FLOAD: + case TYPE_STORE: + case TYPE_FSTORE: + mem_rtx = extract_mem_rtx (insn); + break; + + default: + gcc_unreachable (); + } + + gcc_assert (MEM_P (mem_rtx)); + + /* (mem (reg)) */ + if (REG_P (XEXP (mem_rtx, 0))) + return NULL_RTX; + + plus_rtx = XEXP (mem_rtx, 0); + + switch (GET_CODE (plus_rtx)) + { + case SYMBOL_REF: + case CONST: + case POST_INC: + case POST_DEC: + return NULL_RTX; + + case PLUS: + /* (mem (plus (reg) (const_int))) or + (mem (plus (mult (reg) (const_int 4)) (reg))) */ + if (REG_P (XEXP (plus_rtx, 0))) + offset_rtx = XEXP (plus_rtx, 1); + else + { + gcc_assert (REG_P (XEXP (plus_rtx, 1))); + offset_rtx = XEXP (plus_rtx, 0); + } + + if (ARITHMETIC_P (offset_rtx)) + { + gcc_assert (GET_CODE (offset_rtx) == MULT); + gcc_assert (REG_P (XEXP (offset_rtx, 0))); + offset_rtx = XEXP (offset_rtx, 0); + } + break; + + case LO_SUM: + /* (mem (lo_sum (reg) (symbol_ref)) */ + offset_rtx = XEXP (plus_rtx, 1); + break; + + case POST_MODIFY: + /* (mem (post_modify (reg) (plus (reg) (reg / const_int)))) */ + gcc_assert (REG_P (XEXP (plus_rtx, 0))); + plus_rtx = XEXP (plus_rtx, 1); + gcc_assert (GET_CODE (plus_rtx) == PLUS); + offset_rtx = XEXP (plus_rtx, 0); + break; + + default: + gcc_unreachable (); + } + + return offset_rtx; +} + +/* Extract the register of the shift operand from an ALU_SHIFT rtx. */ +rtx +extract_shift_reg (rtx alu_shift_rtx) +{ + alu_shift_rtx = extract_pattern_from_insn (alu_shift_rtx); + + rtx alu_rtx = SET_SRC (alu_shift_rtx); + rtx shift_rtx; + + /* Various forms of ALU_SHIFT can be made by the combiner. + See the difference between add_slli and sub_slli in nds32.md. */ + if (REG_P (XEXP (alu_rtx, 0))) + shift_rtx = XEXP (alu_rtx, 1); + else + shift_rtx = XEXP (alu_rtx, 0); + + return XEXP (shift_rtx, 0); +} + +/* Check if INSN is a movd44 insn. */ +bool +movd44_insn_p (rtx_insn *insn) +{ + if (get_attr_type (insn) == TYPE_ALU + && (INSN_CODE (insn) == CODE_FOR_move_di + || INSN_CODE (insn) == CODE_FOR_move_df)) + { + rtx body = PATTERN (insn); + gcc_assert (GET_CODE (body) == SET); + + rtx src = SET_SRC (body); + rtx dest = SET_DEST (body); + + if ((REG_P (src) || GET_CODE (src) == SUBREG) + && (REG_P (dest) || GET_CODE (dest) == SUBREG)) + return true; + + return false; + } + + return false; +} + +/* Extract the second result (odd reg) of a movd44 insn. */ +rtx +extract_movd44_odd_reg (rtx_insn *insn) +{ + gcc_assert (movd44_insn_p (insn)); + + rtx def_reg = SET_DEST (PATTERN (insn)); + machine_mode mode; + + gcc_assert (REG_P (def_reg) || GET_CODE (def_reg) == SUBREG); + switch (GET_MODE (def_reg)) + { + case E_DImode: + mode = SImode; + break; + + case E_DFmode: + mode = SFmode; + break; + + default: + gcc_unreachable (); + } + + return gen_highpart (mode, def_reg); +} + +/* Extract the rtx representing non-accumulation operands of a MAC insn. */ +rtx +extract_mac_non_acc_rtx (rtx_insn *insn) +{ + rtx exp = SET_SRC (PATTERN (insn)); + + switch (get_attr_type (insn)) + { + case TYPE_MAC: + case TYPE_DMAC: + if (REG_P (XEXP (exp, 0))) + return XEXP (exp, 1); + else + return XEXP (exp, 0); + + default: + gcc_unreachable (); + } +} + +/* Check if the DIV insn needs two write ports. */ +bool +divmod_p (rtx_insn *insn) +{ + gcc_assert (get_attr_type (insn) == TYPE_DIV); + + if (INSN_CODE (insn) == CODE_FOR_divmodsi4 + || INSN_CODE (insn) == CODE_FOR_udivmodsi4) + return true; + + return false; +} + +/* Extract the rtx representing the branch target to help recognize + data hazards. */ +rtx +extract_branch_target_rtx (rtx_insn *insn) +{ + gcc_assert (CALL_P (insn) || JUMP_P (insn)); + + rtx body = PATTERN (insn); + + if (GET_CODE (body) == SET) + { + /* RTXs in IF_THEN_ELSE are branch conditions. */ + if (GET_CODE (SET_SRC (body)) == IF_THEN_ELSE) + return NULL_RTX; + + return SET_SRC (body); + } + + if (GET_CODE (body) == CALL) + return XEXP (body, 0); + + if (GET_CODE (body) == PARALLEL) + { + rtx first_rtx = parallel_element (body, 0); + + if (GET_CODE (first_rtx) == SET) + return SET_SRC (first_rtx); + + if (GET_CODE (first_rtx) == CALL) + return XEXP (first_rtx, 0); + } + + /* Handle special cases of bltzal, bgezal and jralnez. */ + if (GET_CODE (body) == COND_EXEC) + { + rtx addr_rtx = XEXP (body, 1); + + if (GET_CODE (addr_rtx) == SET) + return SET_SRC (addr_rtx); + + if (GET_CODE (addr_rtx) == PARALLEL) + { + rtx first_rtx = parallel_element (addr_rtx, 0); + + if (GET_CODE (first_rtx) == SET) + { + rtx call_rtx = SET_SRC (first_rtx); + gcc_assert (GET_CODE (call_rtx) == CALL); + + return XEXP (call_rtx, 0); + } + + if (GET_CODE (first_rtx) == CALL) + return XEXP (first_rtx, 0); + } + } + + gcc_unreachable (); +} + +/* Extract the rtx representing the branch condition to help recognize + data hazards. */ +rtx +extract_branch_condition_rtx (rtx_insn *insn) +{ + gcc_assert (CALL_P (insn) || JUMP_P (insn)); + + rtx body = PATTERN (insn); + + if (GET_CODE (body) == SET) + { + rtx if_then_else_rtx = SET_SRC (body); + + if (GET_CODE (if_then_else_rtx) == IF_THEN_ELSE) + return XEXP (if_then_else_rtx, 0); + + return NULL_RTX; + } + + if (GET_CODE (body) == COND_EXEC) + return XEXP (body, 0); + + return NULL_RTX; +} + +} // namespace nds32