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);
+}