Mercurial > hg > CbC > CbC_gcc
diff gcc/config/nds32/nds32-relax-opt.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-relax-opt.c Thu Oct 25 07:37:49 2018 +0900 @@ -0,0 +1,581 @@ +/* relax-opt pass 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 "stringpool.h" +#include "attribs.h" +#include "df.h" +#include "memmodel.h" +#include "tm_p.h" +#include "optabs.h" /* For GEN_FCN. */ +#include "regs.h" +#include "emit-rtl.h" +#include "recog.h" +#include "diagnostic-core.h" +#include "stor-layout.h" +#include "varasm.h" +#include "calls.h" +#include "output.h" +#include "explow.h" +#include "expr.h" +#include "tm-constrs.h" +#include "builtins.h" +#include "cpplib.h" +#include "insn-attr.h" +#include "cfgrtl.h" +#include "tree-pass.h" + +using namespace nds32; + +/* This is used to create unique relax hint id value. + The initial value is 0. */ +static int relax_group_id = 0; + +/* Group the following pattern as relax candidates: + + 1. sethi $ra, hi20(sym) + ori $ra, $ra, lo12(sym) + ==> + addi.gp $ra, sym + + 2. sethi $ra, hi20(sym) + lwi $rb, [$ra + lo12(sym)] + ==> + lwi.gp $rb, [(sym)] + + 3. sethi $ra, hi20(sym) + ori $ra, $ra, lo12(sym) + lwi $rb, [$ra] + swi $rc, [$ra] + ==> + lwi37 $rb, [(sym)] + swi37 $rc, [(sym)] */ + +/* Return true if is load/store with REG addressing mode + and memory mode is SImode. */ +static bool +nds32_reg_base_load_store_p (rtx_insn *insn) +{ + rtx mem_src = NULL_RTX; + + switch (get_attr_type (insn)) + { + case TYPE_LOAD: + mem_src = SET_SRC (PATTERN (insn)); + break; + case TYPE_STORE: + mem_src = SET_DEST (PATTERN (insn)); + break; + default: + break; + } + + /* Find load/store insn with addressing mode is REG. */ + if (mem_src != NULL_RTX) + { + if ((GET_CODE (mem_src) == ZERO_EXTEND) + || (GET_CODE (mem_src) == SIGN_EXTEND)) + mem_src = XEXP (mem_src, 0); + + if (GET_CODE (XEXP (mem_src, 0)) == REG) + return true; + } + + return false; +} + +/* Return true if insn is a sp/fp base or sp/fp plus load-store instruction. */ + +static bool +nds32_sp_base_or_plus_load_store_p (rtx_insn *insn) +{ + rtx mem_src = NULL_RTX; + + switch (get_attr_type (insn)) + { + case TYPE_LOAD: + mem_src = SET_SRC (PATTERN (insn)); + break; + case TYPE_STORE: + mem_src = SET_DEST (PATTERN (insn)); + break; + default: + break; + } + /* Find load/store insn with addressing mode is REG. */ + if (mem_src != NULL_RTX) + { + if ((GET_CODE (mem_src) == ZERO_EXTEND) + || (GET_CODE (mem_src) == SIGN_EXTEND)) + mem_src = XEXP (mem_src, 0); + + if ((GET_CODE (XEXP (mem_src, 0)) == PLUS)) + mem_src = XEXP (mem_src, 0); + + if (REG_P (XEXP (mem_src, 0)) + && ((frame_pointer_needed + && REGNO (XEXP (mem_src, 0)) == FP_REGNUM) + || REGNO (XEXP (mem_src, 0)) == SP_REGNUM)) + return true; + } + + return false; +} + +/* Return true if is load with [REG + REG/CONST_INT] addressing mode. */ +static bool +nds32_plus_reg_load_store_p (rtx_insn *insn) +{ + rtx mem_src = NULL_RTX; + + switch (get_attr_type (insn)) + { + case TYPE_LOAD: + mem_src = SET_SRC (PATTERN (insn)); + break; + case TYPE_STORE: + mem_src = SET_DEST (PATTERN (insn)); + break; + default: + break; + } + + /* Find load/store insn with addressing mode is [REG + REG/CONST]. */ + if (mem_src != NULL_RTX) + { + if ((GET_CODE (mem_src) == ZERO_EXTEND) + || (GET_CODE (mem_src) == SIGN_EXTEND)) + mem_src = XEXP (mem_src, 0); + + if ((GET_CODE (XEXP (mem_src, 0)) == PLUS)) + mem_src = XEXP (mem_src, 0); + else + return false; + + if (GET_CODE (XEXP (mem_src, 0)) == REG) + return true; + + } + + return false; +} + +/* Return true if x is const and the referance is ict symbol. */ +static bool +nds32_ict_const_p (rtx x) +{ + if (GET_CODE (x) == CONST) + { + x = XEXP (x, 0); + return nds32_indirect_call_referenced_p (x); + } + return FALSE; +} + +/* Group the following pattern as relax candidates: + + GOT: + sethi $ra, hi20(sym) + ori $ra, $ra, lo12(sym) + lw $rb, [$ra + $gp] + + GOTOFF, TLSLE: + sethi $ra, hi20(sym) + ori $ra, $ra, lo12(sym) + LS $rb, [$ra + $gp] + + GOTOFF, TLSLE: + sethi $ra, hi20(sym) + ori $ra, $ra, lo12(sym) + add $rb, $ra, $gp($tp) + + Initial GOT table: + sethi $gp,hi20(sym) + ori $gp, $gp, lo12(sym) + add5.pc $gp */ + +static auto_vec<rtx_insn *, 32> nds32_group_infos; +/* Group the PIC and TLS relax candidate instructions for linker. */ +static bool +nds32_pic_tls_group (rtx_insn *def_insn, + enum nds32_relax_insn_type relax_type, + int sym_type) +{ + df_ref def_record; + df_link *link; + rtx_insn *use_insn = NULL; + rtx pat, new_pat; + def_record = DF_INSN_DEFS (def_insn); + for (link = DF_REF_CHAIN (def_record); link; link = link->next) + { + if (!DF_REF_INSN_INFO (link->ref)) + continue; + + use_insn = DF_REF_INSN (link->ref); + + /* Skip if define insn and use insn not in the same basic block. */ + if (!dominated_by_p (CDI_DOMINATORS, + BLOCK_FOR_INSN (use_insn), + BLOCK_FOR_INSN (def_insn))) + return FALSE; + + /* Skip if use_insn not active insn. */ + if (!active_insn_p (use_insn)) + return FALSE; + + switch (relax_type) + { + case RELAX_ORI: + + /* GOTOFF, TLSLE: + sethi $ra, hi20(sym) + ori $ra, $ra, lo12(sym) + add $rb, $ra, $gp($tp) */ + if ((sym_type == UNSPEC_TLSLE + || sym_type == UNSPEC_GOTOFF) + && (recog_memoized (use_insn) == CODE_FOR_addsi3)) + { + pat = XEXP (PATTERN (use_insn), 1); + new_pat = + gen_rtx_UNSPEC (SImode, + gen_rtvec (2, XEXP (pat, 0), XEXP (pat, 1)), + UNSPEC_ADD32); + validate_replace_rtx (pat, new_pat, use_insn); + nds32_group_infos.safe_push (use_insn); + } + else if (nds32_plus_reg_load_store_p (use_insn) + && !nds32_sp_base_or_plus_load_store_p (use_insn)) + nds32_group_infos.safe_push (use_insn); + else + return FALSE; + break; + + default: + return FALSE; + } + } + return TRUE; +} + +static int +nds32_pic_tls_symbol_type (rtx x) +{ + x = XEXP (SET_SRC (PATTERN (x)), 1); + + if (GET_CODE (x) == CONST) + { + x = XEXP (x, 0); + + if (GET_CODE (x) == PLUS) + x = XEXP (x, 0); + + return XINT (x, 1); + } + + return XINT (x, 1); +} + +/* Group the relax candidates with group id. */ +static void +nds32_group_insns (rtx_insn *sethi) +{ + df_ref def_record, use_record; + df_link *link; + rtx_insn *use_insn = NULL; + rtx group_id; + bool valid; + + def_record = DF_INSN_DEFS (sethi); + + for (link = DF_REF_CHAIN (def_record); link; link = link->next) + { + if (!DF_REF_INSN_INFO (link->ref)) + continue; + + use_insn = DF_REF_INSN (link->ref); + + /* Skip if define insn and use insn not in the same basic block. */ + if (!dominated_by_p (CDI_DOMINATORS, + BLOCK_FOR_INSN (use_insn), + BLOCK_FOR_INSN (sethi))) + return; + + /* Skip if the low-part used register is from different high-part + instructions. */ + use_record = DF_INSN_USES (use_insn); + if (DF_REF_CHAIN (use_record) && DF_REF_CHAIN (use_record)->next) + return; + + /* Skip if use_insn not active insn. */ + if (!active_insn_p (use_insn)) + return; + + /* Initial use_insn_type. */ + if (!(recog_memoized (use_insn) == CODE_FOR_lo_sum + || nds32_symbol_load_store_p (use_insn) + || (nds32_reg_base_load_store_p (use_insn) + &&!nds32_sp_base_or_plus_load_store_p (use_insn)))) + return; + } + + group_id = GEN_INT (relax_group_id); + /* Insert .relax_* directive for sethi. */ + emit_insn_before (gen_relax_group (group_id), sethi); + + /* Scan the use insns and insert the directive. */ + for (link = DF_REF_CHAIN (def_record); link; link = link->next) + { + if (!DF_REF_INSN_INFO (link->ref)) + continue; + + use_insn = DF_REF_INSN (link->ref); + + /* Insert .relax_* directive. */ + if (active_insn_p (use_insn)) + emit_insn_before (gen_relax_group (group_id), use_insn); + + /* Find ori ra, ra, unspec(symbol) instruction. */ + if (use_insn != NULL + && recog_memoized (use_insn) == CODE_FOR_lo_sum + && !nds32_const_unspec_p (XEXP (SET_SRC (PATTERN (use_insn)), 1))) + { + int sym_type = nds32_pic_tls_symbol_type (use_insn); + valid = nds32_pic_tls_group (use_insn, RELAX_ORI, sym_type); + + /* Insert .relax_* directive. */ + while (!nds32_group_infos.is_empty ()) + { + use_insn = nds32_group_infos.pop (); + if (valid) + emit_insn_before (gen_relax_group (group_id), use_insn); + } + } + } + + relax_group_id++; +} + +/* Convert relax group id in rtl. */ + +static void +nds32_group_tls_insn (rtx insn) +{ + rtx pat = PATTERN (insn); + rtx unspec_relax_group = XEXP (XVECEXP (pat, 0, 1), 0); + + while (GET_CODE (pat) != SET && GET_CODE (pat) == PARALLEL) + { + pat = XVECEXP (pat, 0, 0); + } + + if (GET_CODE (unspec_relax_group) == UNSPEC + && XINT (unspec_relax_group, 1) == UNSPEC_VOLATILE_RELAX_GROUP) + { + XVECEXP (unspec_relax_group, 0, 0) = GEN_INT (relax_group_id); + } + + relax_group_id++; +} + +static bool +nds32_float_reg_load_store_p (rtx_insn *insn) +{ + rtx pat = PATTERN (insn); + + if (get_attr_type (insn) == TYPE_FLOAD + && GET_CODE (pat) == SET + && (GET_MODE (XEXP (pat, 0)) == SFmode + || GET_MODE (XEXP (pat, 0)) == DFmode) + && MEM_P (XEXP (pat, 1))) + { + rtx addr = XEXP (XEXP (pat, 1), 0); + + /* [$ra] */ + if (REG_P (addr)) + return true; + /* [$ra + offset] */ + if (GET_CODE (addr) == PLUS + && REG_P (XEXP (addr, 0)) + && CONST_INT_P (XEXP (addr, 1))) + return true; + } + return false; +} + + +/* Group float load-store instructions: + la $ra, symbol + flsi $rt, [$ra + offset] */ + +static void +nds32_group_float_insns (rtx_insn *insn) +{ + df_ref def_record, use_record; + df_link *link; + rtx_insn *use_insn = NULL; + rtx group_id; + + def_record = DF_INSN_DEFS (insn); + + for (link = DF_REF_CHAIN (def_record); link; link = link->next) + { + if (!DF_REF_INSN_INFO (link->ref)) + continue; + + use_insn = DF_REF_INSN (link->ref); + + /* Skip if define insn and use insn not in the same basic block. */ + if (!dominated_by_p (CDI_DOMINATORS, + BLOCK_FOR_INSN (use_insn), + BLOCK_FOR_INSN (insn))) + return; + + /* Skip if the low-part used register is from different high-part + instructions. */ + use_record = DF_INSN_USES (use_insn); + if (DF_REF_CHAIN (use_record) && DF_REF_CHAIN (use_record)->next) + return; + + /* Skip if use_insn not active insn. */ + if (!active_insn_p (use_insn)) + return; + + if (!nds32_float_reg_load_store_p (use_insn) + || find_post_update_rtx (use_insn) != -1) + return; + } + + group_id = GEN_INT (relax_group_id); + /* Insert .relax_* directive for insn. */ + emit_insn_before (gen_relax_group (group_id), insn); + + /* Scan the use insns and insert the directive. */ + for (link = DF_REF_CHAIN (def_record); link; link = link->next) + { + if (!DF_REF_INSN_INFO (link->ref)) + continue; + + use_insn = DF_REF_INSN (link->ref); + + /* Insert .relax_* directive. */ + emit_insn_before (gen_relax_group (group_id), use_insn); + } + + relax_group_id++; +} + +/* Group the relax candidate instructions for linker. */ +static void +nds32_relax_group (void) +{ + rtx_insn *insn; + + compute_bb_for_insn (); + + df_chain_add_problem (DF_DU_CHAIN | DF_UD_CHAIN); + df_insn_rescan_all (); + df_analyze (); + df_set_flags (DF_DEFER_INSN_RESCAN); + calculate_dominance_info (CDI_DOMINATORS); + + insn = get_insns (); + gcc_assert (NOTE_P (insn)); + + for (insn = next_active_insn (insn); insn; insn = next_active_insn (insn)) + { + if (NONJUMP_INSN_P (insn)) + { + /* Find sethi ra, symbol instruction. */ + if (recog_memoized (insn) == CODE_FOR_sethi + && nds32_symbolic_operand (XEXP (SET_SRC (PATTERN (insn)), 0), + SImode) + && !nds32_ict_const_p (XEXP (SET_SRC (PATTERN (insn)), 0))) + nds32_group_insns (insn); + else if (recog_memoized (insn) == CODE_FOR_tls_ie) + nds32_group_tls_insn (insn); + else if (TARGET_FPU_SINGLE + && recog_memoized (insn) == CODE_FOR_move_addr + && !nds32_ict_const_p (XEXP (SET_SRC (PATTERN (insn)), 0))) + { + nds32_group_float_insns (insn); + } + } + else if (CALL_P (insn) && recog_memoized (insn) == CODE_FOR_tls_desc) + { + nds32_group_tls_insn (insn); + } + } + + /* We must call df_finish_pass manually because it should be invoked before + BB information is destroyed. Hence we cannot set the TODO_df_finish flag + to the pass manager. */ + df_insn_rescan_all (); + df_finish_pass (false); + free_dominance_info (CDI_DOMINATORS); +} + +static unsigned int +nds32_relax_opt (void) +{ + if (TARGET_RELAX_HINT) + nds32_relax_group (); + return 1; +} + +const pass_data pass_data_nds32_relax_opt = +{ + RTL_PASS, /* type */ + "relax_opt", /* name */ + OPTGROUP_NONE, /* optinfo_flags */ + TV_MACH_DEP, /* tv_id */ + 0, /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + TODO_df_finish, /* todo_flags_finish */ +}; + +class pass_nds32_relax_opt : public rtl_opt_pass +{ +public: + pass_nds32_relax_opt (gcc::context *ctxt) + : rtl_opt_pass (pass_data_nds32_relax_opt, ctxt) + {} + + /* opt_pass methods: */ + bool gate (function *) { return TARGET_RELAX_HINT; } + unsigned int execute (function *) { return nds32_relax_opt (); } +}; + +rtl_opt_pass * +make_pass_nds32_relax_opt (gcc::context *ctxt) +{ + return new pass_nds32_relax_opt (ctxt); +}