diff mbox

[13/15] alpha: Use hashing infrastructure for generating constants

Message ID 1439341904-9345-14-git-send-email-rth@redhat.com
State New
Headers show

Commit Message

Richard Henderson Aug. 12, 2015, 1:11 a.m. UTC
---
	* config/alpha/alpha.c: Include genimm-hash.h
	(genimm_alpha): New class.
	(genimm_alpha::genimm_alpha): New.
	(genimm_alpha::set0, genimm_alpha::opN): New.
	(genimm_alpha::exam_simple): New.
	(genimm_alpha::exam_sub): New.
	(genimm_alpha::exam_search): Extract from the body of the
	old alpha_emit_set_const_1.
	(genimm_alpha::exam_full): New.
	(genimm_alpha::exam_generate): New.
	(alpha_emit_set_const_1): Remove.
	(alpha_emit_set_const): Rewrite using genimm_hash.
	(alpha_legitimate_constant_p): Use alpha_emit_set_const.
---
 gcc/config/alpha/alpha.c | 497 ++++++++++++++++++++++++-----------------------
 1 file changed, 250 insertions(+), 247 deletions(-)
diff mbox

Patch

diff --git a/gcc/config/alpha/alpha.c b/gcc/config/alpha/alpha.c
index ca07cc7..6933601 100644
--- a/gcc/config/alpha/alpha.c
+++ b/gcc/config/alpha/alpha.c
@@ -77,6 +77,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "params.h"
 #include "builtins.h"
 #include "rtl-iter.h"
+#include "genimm-hash.h"
 
 /* This file should be included last.  */
 #include "target-def.h"
@@ -1726,249 +1727,277 @@  alpha_set_memflags (rtx seq, rtx ref)
       gcc_unreachable ();
 }
 
-static rtx alpha_emit_set_const (rtx, machine_mode, HOST_WIDE_INT,
-				 int, bool);
+namespace {
 
-/* Internal routine for alpha_emit_set_const to check for N or below insns.
-   If NO_OUTPUT is true, then we only check to see if N insns are possible,
-   and return pc_rtx if successful.  */
+/* All constants can be constructed in 5 insns.  */
+struct genimm_alpha : genimm_base<rtx_code, 5>
+{
+  static const int max_simple = 2;
 
-static rtx
-alpha_emit_set_const_1 (rtx target, machine_mode mode,
-			HOST_WIDE_INT c, int n, bool no_output)
+  genimm_alpha(HOST_WIDE_INT c);
+  void set0 (HOST_WIDE_INT o);
+  void opN (rtx_code r, HOST_WIDE_INT o);
+
+  bool exam_simple (HOST_WIDE_INT c, machine_mode mode, int budget);
+  bool exam_sub (HOST_WIDE_INT c, int budget);
+  bool exam_search (HOST_WIDE_INT c, int budget);
+  void exam_full (HOST_WIDE_INT c);
+  void generate (rtx dest, machine_mode mode) const;
+};
+
+genimm_alpha::genimm_alpha (HOST_WIDE_INT c)
+  : genimm_base (c)
 {
-  HOST_WIDE_INT new_const;
-  int i, bits;
-  /* Use a pseudo if highly optimizing and still generating RTL.  */
-  rtx subtarget
-    = (flag_expensive_optimizations && can_create_pseudo_p () ? 0 : target);
-  rtx temp, insn;
+#ifdef ENABLE_CHECKING
+  code[0] = code[1] = code[2] = code[3] = code[4] = UNKNOWN;
+  op[0] = op[1] = op[2] = op[3] = op[4] = 0;
+#endif
+}
+
+void
+genimm_alpha::set0 (HOST_WIDE_INT o)
+{
+  cost = 1;
+  code[0] = SET;
+  op[0] = o;
+}
+
+void
+genimm_alpha::opN (rtx_code r, HOST_WIDE_INT o)
+{
+  int n = cost++;
+  gcc_checking_assert (n > 0 && n < max_cost);
+  code[n] = r;
+  op[n] = o;
+}
 
-  /* If this is a sign-extended 32-bit constant, we can do this in at most
-     three insns, so do it if we have enough insns left.  */
+/* Only handle simple one and two insn constants.  */
 
-  if (c >> 31 == -1 || c >> 31 == 0)
+bool
+genimm_alpha::exam_simple (HOST_WIDE_INT c, machine_mode mode, int budget)
+{
+  HOST_WIDE_INT lo = sext_hwi (c, 16);
+  HOST_WIDE_INT hi = sext_hwi (c - lo, 32);
+
+  if (c == lo || c == hi)
+    {
+      set0 (c);
+      return true;
+    }
+  if (budget >= 2 && (c == hi + lo || mode == SImode))
     {
-      HOST_WIDE_INT low = ((c & 0xffff) ^ 0x8000) - 0x8000;
-      HOST_WIDE_INT tmp1 = c - low;
-      HOST_WIDE_INT high = (((tmp1 >> 16) & 0xffff) ^ 0x8000) - 0x8000;
-      HOST_WIDE_INT extra = 0;
+      set0 (hi);
+      opN (PLUS, lo);
+      return true;
+    }
+  return false;
+}
 
-      /* If HIGH will be interpreted as negative but the constant is
-	 positive, we must adjust it to do two ldha insns.  */
+bool
+genimm_alpha::exam_sub (HOST_WIDE_INT c, int budget)
+{
+  return (exam_simple (c, DImode, budget)
+	  || (budget > 1 && exam_search (c, budget)));
+}
 
-      if ((high & 0x8000) != 0 && c >= 0)
-	{
-	  extra = 0x4000;
-	  tmp1 -= 0x40000000;
-	  high = ((tmp1 >> 16) & 0xffff) - 2 * ((tmp1 >> 16) & 0x8000);
-	}
+/* The body of the recursive search for C within BUDGET.
+   We've already failed exam_simple.  */
 
-      if (c == low || (low == 0 && extra == 0))
+bool
+genimm_alpha::exam_search (HOST_WIDE_INT c, int budget)
+{
+  const int sub_budget = budget - 1;
+  HOST_WIDE_INT test;
+
+  /* Simple subtractions.  */
+  test = sext_hwi (c, 16);
+  if (test != 0)
+    {
+      if (exam_sub (c - test, sub_budget))
 	{
-	  /* We used to use copy_to_suggested_reg (GEN_INT (c), target, mode)
-	     but that meant that we can't handle INT_MIN on 32-bit machines
-	     (like NT/Alpha), because we recurse indefinitely through
-	     emit_move_insn to gen_movdi.  So instead, since we know exactly
-	     what we want, create it explicitly.  */
-
-	  if (no_output)
-	    return pc_rtx;
-	  if (target == NULL)
-	    target = gen_reg_rtx (mode);
-	  emit_insn (gen_rtx_SET (target, GEN_INT (c)));
-	  return target;
+	  opN (PLUS, test);
+	  return true;
 	}
-      else if (n >= 2 + (extra != 0))
+      /* Note that it doesn't help to try LDAH at this point.
+	 Zeroing bits in the middle of C when we know there
+	 are low bits set doesn't produce a simpler constant.  */
+    }
+  else
+    {
+      test = sext_hwi (c, 32);
+      if (test != 0)
 	{
-	  if (no_output)
-	    return pc_rtx;
-	  if (!can_create_pseudo_p ())
+	  if (exam_sub (c - test, sub_budget))
 	    {
-	      emit_insn (gen_rtx_SET (target, GEN_INT (high << 16)));
-	      temp = target;
+	      opN (PLUS, test);
+	      return true;
 	    }
-	  else
-	    temp = copy_to_suggested_reg (GEN_INT (high << 16),
-					  subtarget, mode);
 
-	  /* As of 2002-02-23, addsi3 is only available when not optimizing.
-	     This means that if we go through expand_binop, we'll try to
-	     generate extensions, etc, which will require new pseudos, which
-	     will fail during some split phases.  The SImode add patterns
-	     still exist, but are not named.  So build the insns by hand.  */
-
-	  if (extra != 0)
+	  /* Form constants between 0x80000000 - 0xfffe0000 by splitting
+	     the LDAH into two.  The ordering of operations here should
+	     prefer to load 0x7fff0000 into a register first, which would
+	     then be sharable by other constant loading sequences.  */
+	  HOST_WIDE_INT uhi = c & HOST_WIDE_INT_UC (0xffff0000);
+	  test = 0x7fff0000;
+	  if (uhi == c && uhi - test <= test)
 	    {
-	      if (! subtarget)
-		subtarget = gen_reg_rtx (mode);
-	      insn = gen_rtx_PLUS (mode, temp, GEN_INT (extra << 16));
-	      insn = gen_rtx_SET (subtarget, insn);
-	      emit_insn (insn);
-	      temp = subtarget;
+	      set0 (test);
+	      opN (PLUS, uhi - test);
+	      return true;
 	    }
-
-	  if (target == NULL)
-	    target = gen_reg_rtx (mode);
-	  insn = gen_rtx_PLUS (mode, temp, GEN_INT (low));
-	  insn = gen_rtx_SET (target, insn);
-	  emit_insn (insn);
-	  return target;
 	}
     }
 
-  /* If we couldn't do it that way, try some other methods.  But if we have
-     no instructions left, don't bother.  Likewise, if this is SImode and
-     we can't make pseudos, we can't do anything since the expand_binop
-     and expand_unop calls will widen and try to make pseudos.  */
-
-  if (n == 1 || (mode == SImode && !can_create_pseudo_p ()))
-    return 0;
-
-  /* Next, see if we can load a related constant and then shift and possibly
-     negate it to get the constant we want.  Try this once each increasing
-     numbers of insns.  */
+  /* Try complimenting.  */
+  if (exam_sub (~c, sub_budget))
+    {
+      opN (NOT, 0);
+      return true;
+    }
 
-  for (i = 1; i < n; i++)
+  /* Try to form a constant and do a left shift.  We can do this
+     if some low-order bits are zero.  The bits we are shifting out
+     could be any value, but here we'll just try the zero- and sign-
+     extended forms of the constant.  To try to increase the chance
+     of having the same constant in more than one insn, start at the
+     highest number of bits to shift, but try all possibilities in
+     case a ZAPNOT will be useful.  */
+  for (int bits = ctz_hwi (c); bits > 0; --bits)
     {
-      /* First, see if minus some low bits, we've an easy load of
-	 high bits.  */
+      /* First try with ones being shifted out.  */
+      test = c >> bits;
+      bool ok = exam_sub (test, sub_budget);
+      if (!ok && c < 0)
+	{
+	  /* If that failed, try with zeros being shifted out.  */
+	  test = (unsigned HOST_WIDE_INT)c >> bits;
+	  ok = exam_sub (test, sub_budget);
+	}
+      if (ok)
+	{
+	  opN (ASHIFT, bits);
+	  return true;
+	}
+    }
 
-      new_const = ((c & 0xffff) ^ 0x8000) - 0x8000;
-      if (new_const != 0)
+  /* Try a right shift, shifting in zeros.  */
+  for (int bits = clz_hwi (c); bits > 0; --bits)
+    {
+      test = c << bits;
+      bool ok = exam_sub (test, sub_budget);
+      if (!ok)
 	{
-          temp = alpha_emit_set_const (subtarget, mode, c - new_const, i, no_output);
-	  if (temp)
-	    {
-	      if (no_output)
-		return temp;
-	      return expand_binop (mode, add_optab, temp, GEN_INT (new_const),
-				   target, 0, OPTAB_WIDEN);
-	    }
+	  test |= (HOST_WIDE_INT_1U << bits) - 1;
+	  ok = exam_sub (test, sub_budget);
+	}
+      if (ok)
+	{
+	  opN (LSHIFTRT, bits);
+	  return true;
 	}
+    }
 
-      /* Next try complementing.  */
-      temp = alpha_emit_set_const (subtarget, mode, ~c, i, no_output);
-      if (temp)
+  /* Try a right shift, shifting in ones.  */
+  for (int bits = clz_hwi (~c) - 1; bits > 0; --bits)
+    {
+      test = c << bits;
+      bool ok = exam_sub (test, sub_budget);
+      if (!ok)
 	{
-	  if (no_output)
-	    return temp;
-	  return expand_unop (mode, one_cmpl_optab, temp, target, 0);
+	  test |= (HOST_WIDE_INT_1U << bits) - 1;
+	  ok = exam_sub (test, sub_budget);
 	}
+      if (ok)
+	{
+	  opN (ASHIFTRT, bits);
+	  return true;
+	}
+    }
 
-      /* Next try to form a constant and do a left shift.  We can do this
-	 if some low-order bits are zero; the exact_log2 call below tells
-	 us that information.  The bits we are shifting out could be any
-	 value, but here we'll just try the 0- and sign-extended forms of
-	 the constant.  To try to increase the chance of having the same
-	 constant in more than one insn, start at the highest number of
-	 bits to shift, but try all possibilities in case a ZAPNOT will
-	 be useful.  */
-
-      bits = exact_log2 (c & -c);
-      if (bits > 0)
-	for (; bits > 0; bits--)
-	  {
-	    new_const = c >> bits;
-	    temp = alpha_emit_set_const (subtarget, mode, new_const, i, no_output);
-	    if (!temp && c < 0)
-	      {
-		new_const = (unsigned HOST_WIDE_INT)c >> bits;
-		temp = alpha_emit_set_const (subtarget, mode, new_const,
-					     i, no_output);
-	      }
-	    if (temp)
-	      {
-		if (no_output)
-		  return temp;
-	        return expand_binop (mode, ashl_optab, temp, GEN_INT (bits),
-				     target, 0, OPTAB_WIDEN);
-	      }
-	  }
+  /* Try loading a value and zapping bytes.  */
+  test = c;
+  for (int i = 0; i < 64; i += 8)
+    {
+      HOST_WIDE_INT byte = HOST_WIDE_INT_UC (0xff) << i;
+      if ((c & byte) == 0)
+	test |= byte;
+    }
+  if (test != c && exam_sub (test, sub_budget))
+    {
+      opN (AND, c | ~test);
+      return true;
+    }
 
-      /* Now try high-order zero bits.  Here we try the shifted-in bits as
-	 all zero and all ones.  Be careful to avoid shifting outside the
-	 mode and to avoid shifting outside the host wide int size.  */
+  return false;
+}
 
-      bits = (MIN (HOST_BITS_PER_WIDE_INT, GET_MODE_SIZE (mode) * 8)
-	      - floor_log2 (c) - 1);
-      if (bits > 0)
-	for (; bits > 0; bits--)
-	  {
-	    new_const = c << bits;
-	    temp = alpha_emit_set_const (subtarget, mode, new_const, i, no_output);
-	    if (!temp)
-	      {
-		new_const = (c << bits) | ((HOST_WIDE_INT_1U << bits) - 1);
-	        temp = alpha_emit_set_const (subtarget, mode, new_const,
-					     i, no_output);
-	      }
-	    if (temp)
-	      {
-		if (no_output)
-		  return temp;
-		return expand_binop (mode, lshr_optab, temp, GEN_INT (bits),
-				     target, 1, OPTAB_WIDEN);
-	      }
-	  }
+/* Only handle full 64-bit constants requiring 5 insns.  */
 
-      /* Now try high-order 1 bits.  We get that with a sign-extension.
-	 But one bit isn't enough here.  Be careful to avoid shifting outside
-	 the mode and to avoid shifting outside the host wide int size.  */
+void
+genimm_alpha::exam_full (HOST_WIDE_INT c)
+{
+  HOST_WIDE_INT lo = sext_hwi (c, 16);
+  HOST_WIDE_INT hi = sext_hwi (c - lo, 32);
 
-      bits = (MIN (HOST_BITS_PER_WIDE_INT, GET_MODE_SIZE (mode) * 8)
-	      - floor_log2 (~ c) - 2);
-      if (bits > 0)
-	for (; bits > 0; bits--)
-	  {
-	    new_const = c << bits;
-	    temp = alpha_emit_set_const (subtarget, mode, new_const, i, no_output);
-	    if (!temp)
-	      {
-		new_const = (c << bits) | ((HOST_WIDE_INT_1U << bits) - 1);
-	        temp = alpha_emit_set_const (subtarget, mode, new_const,
-					     i, no_output);
-	      }
-	    if (temp)
-	      {
-		if (no_output)
-		  return temp;
-		return expand_binop (mode, ashr_optab, temp, GEN_INT (bits),
-				     target, 0, OPTAB_WIDEN);
-	      }
-	  }
-    }
+  bool ok = exam_simple ((c - hi - lo) >> 32, DImode, max_simple);
+  gcc_assert (ok);
 
-  /* Finally, see if can load a value into the target that is the same as the
-     constant except that all bytes that are 0 are changed to be 0xff.  If we
-     can, then we can do a ZAPNOT to obtain the desired constant.  */
+  opN (ASHIFT, 32);
+  if (hi)
+    opN (PLUS, hi);
+  if (lo)
+    opN (PLUS, lo);
+}
 
-  new_const = c;
-  for (i = 0; i < 64; i += 8)
-    if ((new_const & ((HOST_WIDE_INT) 0xff << i)) == 0)
-      new_const |= (HOST_WIDE_INT) 0xff << i;
+/* Emit insns for the generated recipe.  */
 
-  /* We are only called for SImode and DImode.  If this is SImode, ensure that
-     we are sign extended to a full word.  */
+void
+genimm_alpha::generate (rtx dest, machine_mode mode) const
+{
+  bool do_subtargets = optimize && can_create_pseudo_p ();
+  rtx op1 = NULL;
+  rtx_insn *insn = NULL;
+  int i, n = cost;
 
-  if (mode == SImode)
-    new_const = ((new_const & 0xffffffff) ^ 0x80000000) - 0x80000000;
+  gcc_checking_assert (n >= 1 && n <= max_cost);
+  gcc_checking_assert (code[0] == SET);
 
-  if (new_const != c)
+  for (i = 0; i < n; ++i)
     {
-      temp = alpha_emit_set_const (subtarget, mode, new_const, n - 1, no_output);
-      if (temp)
+      rtx sub = (do_subtargets && i + 1 < n ? gen_reg_rtx (mode) : dest);
+      rtx op2 = GEN_INT (op[i]);
+      rtx_code r = code[i];
+      rtx x;
+
+      switch (r)
 	{
-	  if (no_output)
-	    return temp;
-	  return expand_binop (mode, and_optab, temp, GEN_INT (c | ~ new_const),
-			       target, 0, OPTAB_WIDEN);
+	case SET:
+	  x = op2;
+	  break;
+	case AND:
+	case PLUS:
+	case ASHIFT:
+	case ASHIFTRT:
+	case LSHIFTRT:
+	  x = gen_rtx_fmt_ee (r, mode, op1, op2);
+	  break;
+	case NOT:
+	  x = gen_rtx_fmt_e (r, mode, op1);
+	  break;
+	default:
+	  gcc_unreachable ();
 	}
+
+      insn = emit_insn (gen_rtx_SET (sub, x));
+      op1 = sub;
     }
 
-  return 0;
+  if (n > 1)
+    set_unique_reg_note (insn, REG_EQUAL, GEN_INT (value));
 }
 
+} // anon namespace
+
 /* Try to output insns to set TARGET equal to the constant C if it can be
    done in less than N insns.  Do all computations in MODE.  Returns the place
    where the output has been placed if it can be done and the insns have been
@@ -1976,62 +2005,36 @@  alpha_emit_set_const_1 (rtx target, machine_mode mode,
    insns and emitted.  */
 
 static rtx
-alpha_emit_set_const (rtx target, machine_mode mode,
+alpha_emit_set_const (rtx target, machine_mode origmode,
 		      HOST_WIDE_INT c, int n, bool no_output)
 {
-  machine_mode orig_mode = mode;
-  rtx orig_target = target;
-  rtx result = 0;
-  int i;
+  machine_mode mode = origmode;
+  if (mode == V8QImode || mode == V4HImode || mode == V2SImode)
+    mode = DImode;
 
-  /* If we can't make any pseudos, TARGET is an SImode hard register, we
-     can't load this constant in one insn, do this in DImode.  */
-  if (!can_create_pseudo_p () && mode == SImode
-      && REG_P (target) && REGNO (target) < FIRST_PSEUDO_REGISTER)
-    {
-      result = alpha_emit_set_const_1 (target, mode, c, 1, no_output);
-      if (result)
-	return result;
+  genimm_alpha data = genimm_hash<genimm_alpha>::hash (c, mode);
+  if (data.cost > n)
+    return NULL;
 
-      target = no_output ? NULL : gen_lowpart (DImode, target);
-      mode = DImode;
-    }
-  else if (mode == V8QImode || mode == V4HImode || mode == V2SImode)
-    {
-      target = no_output ? NULL : gen_lowpart (DImode, target);
-      mode = DImode;
-    }
+  /* If we're not emitting output, we only need return a nonnull value.  */
+  if (no_output)
+    return pc_rtx;
 
-  /* Try 1 insn, then 2, then up to N.  */
-  for (i = 1; i <= n; i++)
+  if (origmode == mode)
+    data.generate (target, mode);
+  else
     {
-      result = alpha_emit_set_const_1 (target, mode, c, i, no_output);
-      if (result)
+      rtx low = gen_lowpart (mode, target);
+      if (can_create_pseudo_p ())
 	{
-	  rtx_insn *insn;
-	  rtx set;
-
-	  if (no_output)
-	    return result;
-
-	  insn = get_last_insn ();
-	  set = single_set (insn);
-	  if (! CONSTANT_P (SET_SRC (set)))
-	    set_unique_reg_note (get_last_insn (), REG_EQUAL, GEN_INT (c));
-	  break;
+	  target = gen_reg_rtx (mode);
+	  data.generate (target, mode);
+	  emit_move_insn (low, target);
 	}
+      else
+	data.generate (low, mode);
     }
-
-  /* Allow for the case where we changed the mode of TARGET.  */
-  if (result)
-    {
-      if (result == target)
-	result = orig_target;
-      else if (mode != orig_mode)
-	result = gen_lowpart (orig_mode, result);
-    }
-
-  return result;
+  return target;
 }
 
 /* Having failed to find a 3 insn sequence in alpha_emit_set_const,
@@ -2129,7 +2132,7 @@  alpha_legitimate_constant_p (machine_mode mode, rtx x)
       mode = DImode;
       gcc_assert (CONST_WIDE_INT_NUNITS (x) == 2);
       i0 = CONST_WIDE_INT_ELT (x, 1);
-      if (alpha_emit_set_const_1 (NULL_RTX, mode, i0, 3, true) == NULL)
+      if (alpha_emit_set_const (NULL_RTX, mode, i0, 3, true) == NULL)
 	return false;
       i0 = CONST_WIDE_INT_ELT (x, 0);
       goto do_integer;
@@ -2153,7 +2156,7 @@  alpha_legitimate_constant_p (machine_mode mode, rtx x)
 	return true;
       i0 = alpha_extract_integer (x);
     do_integer:
-      return alpha_emit_set_const_1 (NULL_RTX, mode, i0, 3, true) != NULL;
+      return alpha_emit_set_const (NULL_RTX, mode, i0, 3, true) != NULL;
 
     default:
       return false;