diff options
Diffstat (limited to 'gcc/config/nds32/nds32-predicates.c')
-rw-r--r-- | gcc/config/nds32/nds32-predicates.c | 431 |
1 files changed, 431 insertions, 0 deletions
diff --git a/gcc/config/nds32/nds32-predicates.c b/gcc/config/nds32/nds32-predicates.c new file mode 100644 index 00000000000..209b25f4c74 --- /dev/null +++ b/gcc/config/nds32/nds32-predicates.c @@ -0,0 +1,431 @@ +/* Predicate functions of Andes NDS32 cpu for GNU compiler + Copyright (C) 2012-2015 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/>. */ + +/* ------------------------------------------------------------------------ */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tm.h" +#include "hash-set.h" +#include "machmode.h" +#include "vec.h" +#include "double-int.h" +#include "input.h" +#include "alias.h" +#include "symtab.h" +#include "wide-int.h" +#include "inchash.h" +#include "tree.h" +#include "stor-layout.h" +#include "varasm.h" +#include "calls.h" +#include "rtl.h" +#include "regs.h" +#include "hard-reg-set.h" +#include "insn-config.h" /* Required by recog.h. */ +#include "conditions.h" +#include "output.h" +#include "insn-attr.h" /* For DFA state_t. */ +#include "insn-codes.h" /* For CODE_FOR_xxx. */ +#include "reload.h" /* For push_reload(). */ +#include "flags.h" +#include "function.h" +#include "hashtab.h" +#include "statistics.h" +#include "real.h" +#include "fixed-value.h" +#include "insn-config.h" +#include "expmed.h" +#include "dojump.h" +#include "explow.h" +#include "emit-rtl.h" +#include "stmt.h" +#include "expr.h" +#include "recog.h" +#include "diagnostic-core.h" +#include "dominance.h" +#include "cfg.h" +#include "cfgrtl.h" +#include "cfganal.h" +#include "lcm.h" +#include "cfgbuild.h" +#include "cfgcleanup.h" +#include "predict.h" +#include "basic-block.h" +#include "df.h" +#include "tm_p.h" +#include "tm-constrs.h" +#include "optabs.h" /* For GEN_FCN. */ +#include "target.h" +#include "target-def.h" +#include "langhooks.h" /* For add_builtin_function(). */ +#include "ggc.h" +#include "builtins.h" + +/* ------------------------------------------------------------------------ */ + +/* A subroutine that checks multiple load and store + using consecutive registers. + OP is a parallel rtx we would like to check. + LOAD_P indicates whether we are checking load operation. + PAR_INDEX is starting element of parallel rtx. + FIRST_ELT_REGNO is used to tell starting register number. + COUNT helps us to check consecutive register numbers. */ +static bool +nds32_consecutive_registers_load_store_p (rtx op, + bool load_p, + int par_index, + int first_elt_regno, + int count) +{ + int i; + int check_regno; + rtx elt; + rtx elt_reg; + rtx elt_mem; + + for (i = 0; i < count; i++) + { + /* Pick up each element from parallel rtx. */ + elt = XVECEXP (op, 0, i + par_index); + + /* If this element is not a 'set' rtx, return false immediately. */ + if (GET_CODE (elt) != SET) + return false; + + /* Pick up reg and mem of this element. */ + elt_reg = load_p ? SET_DEST (elt) : SET_SRC (elt); + elt_mem = load_p ? SET_SRC (elt) : SET_DEST (elt); + + /* If elt_reg is not a expected reg rtx, return false. */ + if (GET_CODE (elt_reg) != REG || GET_MODE (elt_reg) != SImode) + return false; + /* If elt_mem is not a expected mem rtx, return false. */ + if (GET_CODE (elt_mem) != MEM || GET_MODE (elt_mem) != SImode) + return false; + + /* The consecutive registers should be in (Rb,Rb+1...Re) order. */ + check_regno = first_elt_regno + i; + + /* If the register number is not continuous, return false. */ + if (REGNO (elt_reg) != (unsigned int) check_regno) + return false; + } + + return true; +} + +/* Function to check whether the OP is a valid load/store operation. + This is a helper function for the predicates: + 'nds32_load_multiple_operation' and 'nds32_store_multiple_operation' + in predicates.md file. + + The OP is supposed to be a parallel rtx. + For each element within this parallel rtx: + (set (reg) (mem addr)) is the form for load operation. + (set (mem addr) (reg)) is the form for store operation. + We have to extract reg and mem of every element and + check if the information is valid for multiple load/store operation. */ +bool +nds32_valid_multiple_load_store (rtx op, bool load_p) +{ + int count; + int first_elt_regno; + rtx elt; + + /* Get the counts of elements in the parallel rtx. */ + count = XVECLEN (op, 0); + /* Pick up the first element. */ + elt = XVECEXP (op, 0, 0); + + /* Perform some quick check for the first element in the parallel rtx. */ + if (GET_CODE (elt) != SET + || count <= 1 + || count > 8) + return false; + + /* Pick up regno of first element for further detail checking. + Note that the form is different between load and store operation. */ + if (load_p) + { + if (GET_CODE (SET_DEST (elt)) != REG + || GET_CODE (SET_SRC (elt)) != MEM) + return false; + + first_elt_regno = REGNO (SET_DEST (elt)); + } + else + { + if (GET_CODE (SET_SRC (elt)) != REG + || GET_CODE (SET_DEST (elt)) != MEM) + return false; + + first_elt_regno = REGNO (SET_SRC (elt)); + } + + /* Perform detail check for each element. + Refer to nds32-multiple.md for more information + about following checking. + The starting element of parallel rtx is index 0. */ + if (!nds32_consecutive_registers_load_store_p (op, load_p, 0, + first_elt_regno, + count)) + return false; + + /* Pass all test, this is a valid rtx. */ + return true; +} + +/* Function to check whether the OP is a valid stack push/pop operation. + For a valid stack operation, it must satisfy following conditions: + 1. Consecutive registers push/pop operations. + 2. Valid $fp/$gp/$lp push/pop operations. + 3. The last element must be stack adjustment rtx. + See the prologue/epilogue implementation for details. */ +bool +nds32_valid_stack_push_pop_p (rtx op, bool push_p) +{ + int index; + int total_count; + int rest_count; + int first_regno; + int save_fp, save_gp, save_lp; + rtx elt; + rtx elt_reg; + rtx elt_mem; + rtx elt_plus; + + /* Get the counts of elements in the parallel rtx. */ + total_count = XVECLEN (op, 0); + + /* Perform some quick check for that every element should be 'set'. */ + for (index = 0; index < total_count; index++) + { + elt = XVECEXP (op, 0, index); + if (GET_CODE (elt) != SET) + return false; + } + + /* For push operation, the parallel rtx looks like: + (parallel [(set (mem (plus (reg:SI SP_REGNUM) (const_int -32))) + (reg:SI Rb)) + (set (mem (plus (reg:SI SP_REGNUM) (const_int -28))) + (reg:SI Rb+1)) + ... + (set (mem (plus (reg:SI SP_REGNUM) (const_int -16))) + (reg:SI Re)) + (set (mem (plus (reg:SI SP_REGNUM) (const_int -12))) + (reg:SI FP_REGNUM)) + (set (mem (plus (reg:SI SP_REGNUM) (const_int -8))) + (reg:SI GP_REGNUM)) + (set (mem (plus (reg:SI SP_REGNUM) (const_int -4))) + (reg:SI LP_REGNUM)) + (set (reg:SI SP_REGNUM) + (plus (reg:SI SP_REGNUM) (const_int -32)))]) + + For pop operation, the parallel rtx looks like: + (parallel [(set (reg:SI Rb) + (mem (reg:SI SP_REGNUM))) + (set (reg:SI Rb+1) + (mem (plus (reg:SI SP_REGNUM) (const_int 4)))) + ... + (set (reg:SI Re) + (mem (plus (reg:SI SP_REGNUM) (const_int 16)))) + (set (reg:SI FP_REGNUM) + (mem (plus (reg:SI SP_REGNUM) (const_int 20)))) + (set (reg:SI GP_REGNUM) + (mem (plus (reg:SI SP_REGNUM) (const_int 24)))) + (set (reg:SI LP_REGNUM) + (mem (plus (reg:SI SP_REGNUM) (const_int 28)))) + (set (reg:SI SP_REGNUM) + (plus (reg:SI SP_REGNUM) (const_int 32)))]) */ + + /* 1. Consecutive registers push/pop operations. + We need to calculate how many registers should be consecutive. + The $sp adjustment rtx, $fp push rtx, $gp push rtx, + and $lp push rtx are excluded. */ + + /* Detect whether we have $fp, $gp, or $lp in the parallel rtx. */ + save_fp = reg_mentioned_p (gen_rtx_REG (SImode, FP_REGNUM), op); + save_gp = reg_mentioned_p (gen_rtx_REG (SImode, GP_REGNUM), op); + save_lp = reg_mentioned_p (gen_rtx_REG (SImode, LP_REGNUM), op); + /* Exclude last $sp adjustment rtx. */ + rest_count = total_count - 1; + /* Exclude $fp, $gp, and $lp if they are in the parallel rtx. */ + if (save_fp) + rest_count--; + if (save_gp) + rest_count--; + if (save_lp) + rest_count--; + + if (rest_count > 0) + { + elt = XVECEXP (op, 0, 0); + /* Pick up register element. */ + elt_reg = push_p ? SET_SRC (elt) : SET_DEST (elt); + first_regno = REGNO (elt_reg); + + /* The 'push' operation is a kind of store operation. + The 'pop' operation is a kind of load operation. + Pass corresponding false/true as second argument (bool load_p). + The par_index is supposed to start with index 0. */ + if (!nds32_consecutive_registers_load_store_p (op, + !push_p ? true : false, + 0, + first_regno, + rest_count)) + return false; + } + + /* 2. Valid $fp/$gp/$lp push/pop operations. + Remember to set start index for checking them. */ + + /* The rest_count is the start index for checking $fp/$gp/$lp. */ + index = rest_count; + /* If index < 0, this parallel rtx is definitely + not a valid stack push/pop operation. */ + if (index < 0) + return false; + + /* Check $fp/$gp/$lp one by one. + We use 'push_p' to pick up reg rtx and mem rtx. */ + if (save_fp) + { + elt = XVECEXP (op, 0, index); + elt_mem = push_p ? SET_DEST (elt) : SET_SRC (elt); + elt_reg = push_p ? SET_SRC (elt) : SET_DEST (elt); + index++; + + if (GET_CODE (elt_mem) != MEM + || GET_CODE (elt_reg) != REG + || REGNO (elt_reg) != FP_REGNUM) + return false; + } + if (save_gp) + { + elt = XVECEXP (op, 0, index); + elt_mem = push_p ? SET_DEST (elt) : SET_SRC (elt); + elt_reg = push_p ? SET_SRC (elt) : SET_DEST (elt); + index++; + + if (GET_CODE (elt_mem) != MEM + || GET_CODE (elt_reg) != REG + || REGNO (elt_reg) != GP_REGNUM) + return false; + } + if (save_lp) + { + elt = XVECEXP (op, 0, index); + elt_mem = push_p ? SET_DEST (elt) : SET_SRC (elt); + elt_reg = push_p ? SET_SRC (elt) : SET_DEST (elt); + index++; + + if (GET_CODE (elt_mem) != MEM + || GET_CODE (elt_reg) != REG + || REGNO (elt_reg) != LP_REGNUM) + return false; + } + + /* 3. The last element must be stack adjustment rtx. + Its form of rtx should be: + (set (reg:SI SP_REGNUM) + (plus (reg:SI SP_REGNUM) (const_int X))) + The X could be positive or negative value. */ + + /* Pick up the last element. */ + elt = XVECEXP (op, 0, total_count - 1); + + /* Extract its destination and source rtx. */ + elt_reg = SET_DEST (elt); + elt_plus = SET_SRC (elt); + + /* Check this is (set (stack_reg) (plus stack_reg const)) pattern. */ + if (GET_CODE (elt_reg) != REG + || GET_CODE (elt_plus) != PLUS + || REGNO (elt_reg) != SP_REGNUM) + return false; + + /* Pass all test, this is a valid rtx. */ + return true; +} + +/* Function to check if 'bclr' instruction can be used with IVAL. */ +int +nds32_can_use_bclr_p (int ival) +{ + int one_bit_count; + + /* Calculate the number of 1-bit of (~ival), if there is only one 1-bit, + it means the original ival has only one 0-bit, + So it is ok to perform 'bclr' operation. */ + + one_bit_count = popcount_hwi ((unsigned HOST_WIDE_INT) (~ival)); + + /* 'bclr' is a performance extension instruction. */ + return (TARGET_PERF_EXT && (one_bit_count == 1)); +} + +/* Function to check if 'bset' instruction can be used with IVAL. */ +int +nds32_can_use_bset_p (int ival) +{ + int one_bit_count; + + /* Caculate the number of 1-bit of ival, if there is only one 1-bit, + it is ok to perform 'bset' operation. */ + + one_bit_count = popcount_hwi ((unsigned HOST_WIDE_INT) (ival)); + + /* 'bset' is a performance extension instruction. */ + return (TARGET_PERF_EXT && (one_bit_count == 1)); +} + +/* Function to check if 'btgl' instruction can be used with IVAL. */ +int +nds32_can_use_btgl_p (int ival) +{ + int one_bit_count; + + /* Caculate the number of 1-bit of ival, if there is only one 1-bit, + it is ok to perform 'btgl' operation. */ + + one_bit_count = popcount_hwi ((unsigned HOST_WIDE_INT) (ival)); + + /* 'btgl' is a performance extension instruction. */ + return (TARGET_PERF_EXT && (one_bit_count == 1)); +} + +/* Function to check if 'bitci' instruction can be used with IVAL. */ +int +nds32_can_use_bitci_p (int ival) +{ + /* If we are using V3 ISA, we have 'bitci' instruction. + Try to see if we can present 'andi' semantic with + such 'bit-clear-immediate' operation. + For example, 'andi $r0,$r0,0xfffffffc' can be + presented with 'bitci $r0,$r0,3'. */ + return (TARGET_ISA_V3 + && (ival < 0) + && satisfies_constraint_Iu15 (gen_int_mode (~ival, SImode))); +} + +/* ------------------------------------------------------------------------ */ |