/* 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 . */ /* ------------------------------------------------------------------------ */ #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" /* 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; } /* Group the relax candidates with group id. */ static void nds32_group_insns (rtx sethi) { df_ref def_record, use_record; df_link *link; rtx_insn *use_insn = NULL; rtx group_id; 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); } 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_group_insns (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); }