diff mbox

[04/15] rs6000: Implement set_const_data infrastructure

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

Commit Message

Richard Henderson Aug. 12, 2015, 1:11 a.m. UTC
Implements basic hashing and minimal cost search, but no real
new optimizations on the constants.  That said, there is one
code generation change implied by the searching: we'll find
equal or more efficient alternatives to zero-extending a
32-bit unsigned constant with bit 31 set.  E.g.

		0x80000001ul		0xf0f0f0f0ul

before		lis 3,0x8000		lis 3,0xf0f0
		ori 3,3,1		ori 3,3,0xf0f0
		rldicl 3,3,0,32		rldicl 3,3,0,32

after		li 3,1			li 3,0
		oris 3,3,0x8000		oris 3,3,0xf0f0
					ori 3,3,0xf0f0

Cc: David Edelsohn <dje.gcc@gmail.com>
---
	* genimm-hash.h: New file.
	* config/rs6000/rs6000.c: Include it.
	(genimm_ppc): New class.
	(genimm_ppc::genimm_ppc): New.
	(genimm_ppc::set0, genimm_ppc::opN): New.
	(genimm_ppc::exam_simple): New.
	(genimm_ppc::exam_sub): New.
	(genimm_ppc::exam_search): New.
	(genimm_ppc::exam_full): New.
	(genimm_ppc::generate): New.
	(num_insns_constant_wide): Use genimm_hash<genimm_ppc>.
	(rs6000_emit_set_const_1): New.
	(rs6000_emit_set_const): Use it.
	(rs6000_emit_set_long_const): Remove.
---
 gcc/config/rs6000/rs6000.c | 368 +++++++++++++++++++++++++--------------------
 gcc/genimm-hash.h          | 122 +++++++++++++++
 2 files changed, 328 insertions(+), 162 deletions(-)
 create mode 100644 gcc/genimm-hash.h

Comments

Segher Boessenkool Aug. 12, 2015, 1:53 p.m. UTC | #1
Hi Richard,

You wanted us to read this file...

On Tue, Aug 11, 2015 at 06:11:33PM -0700, Richard Henderson wrote:
> +   -- The fallback generation for the most complex word_mode constants.
> +      The receipe built will be the full MAX_COST insns, as we will
                ^-- typo.


Segher
diff mbox

Patch

diff --git a/gcc/config/rs6000/rs6000.c b/gcc/config/rs6000/rs6000.c
index c25aa60..a864a7e 100644
--- a/gcc/config/rs6000/rs6000.c
+++ b/gcc/config/rs6000/rs6000.c
@@ -83,6 +83,7 @@ 
 #include "builtins.h"
 #include "context.h"
 #include "tree-pass.h"
+#include "genimm-hash.h"
 #if TARGET_XCOFF
 #include "xcoffout.h"  /* get declarations of xcoff_*_section_name */
 #endif
@@ -1107,7 +1108,6 @@  static tree rs6000_handle_longcall_attribute (tree *, tree, tree, int, bool *);
 static tree rs6000_handle_altivec_attribute (tree *, tree, tree, int, bool *);
 static tree rs6000_handle_struct_attribute (tree *, tree, tree, int, bool *);
 static tree rs6000_builtin_vectorized_libmass (tree, tree, tree);
-static void rs6000_emit_set_long_const (rtx, HOST_WIDE_INT);
 static int num_insns_constant_wide (HOST_WIDE_INT);
 static bool rs6000_is_valid_and_mask_wide (unsigned HOST_WIDE_INT val,
 					   machine_mode mode);
@@ -5238,44 +5238,6 @@  direct_return (void)
   return 0;
 }
 
-/* Return the number of instructions it takes to form a constant in an
-   integer register.  */
-
-static int
-num_insns_constant_wide (HOST_WIDE_INT value)
-{
-  /* signed constant loadable with addi */
-  if (((unsigned HOST_WIDE_INT) value + 0x8000) < 0x10000)
-    return 1;
-
-  /* constant loadable with addis */
-  else if ((value & 0xffff) == 0
-	   && (value >> 31 == -1 || value >> 31 == 0))
-    return 1;
-
-  else if (TARGET_POWERPC64)
-    {
-      HOST_WIDE_INT low  = ((value & 0xffffffff) ^ 0x80000000) - 0x80000000;
-      HOST_WIDE_INT high = value >> 31;
-
-      if (high == 0 || high == -1)
-	return 2;
-
-      high >>= 1;
-
-      if (low == 0)
-	return num_insns_constant_wide (high) + 1;
-      else if (high == 0)
-	return num_insns_constant_wide (low) + 1;
-      else
-	return (num_insns_constant_wide (high)
-		+ num_insns_constant_wide (low) + 1);
-    }
-
-  else
-    return 2;
-}
-
 int
 num_insns_constant (rtx op, machine_mode mode)
 {
@@ -8086,6 +8048,204 @@  rs6000_conditional_register_usage (void)
 }
 
 
+namespace {
+
+/* All constants can be constructed in 5 insns.  */
+struct genimm_ppc : genimm_base <rtx_code, 5>
+{
+  static const int max_simple = 2;
+
+  genimm_ppc (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, 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_ppc::genimm_ppc (HOST_WIDE_INT c)
+  : genimm_base (c)
+{
+#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_ppc::set0 (HOST_WIDE_INT o)
+{
+  cost = 1;
+  code[0] = SET;
+  op[0] = o;
+}
+
+void
+genimm_ppc::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;
+}
+
+/* Only handle simple 32-bit sign-extended constants.  */
+
+bool
+genimm_ppc::exam_simple (HOST_WIDE_INT c, machine_mode, int budget)
+{
+  if ((unsigned HOST_WIDE_INT)c + 0x8000 < 0x10000)
+    {
+      set0 (c); /* LI */
+      return true;
+    }
+  if (c >> 31 == -1 || c >> 31 == 0)
+    {
+      int uh0 = c & 0xffff;
+
+      if (uh0 && budget < 2)
+	return false;
+
+      set0 (c - uh0); /* LIS */
+      if (uh0)
+	opN (IOR, uh0);
+      return true;
+    }
+  return false;
+}
+
+bool
+genimm_ppc::exam_sub (HOST_WIDE_INT c, int budget)
+{
+  return (exam_simple (c, DImode, budget)
+	  || (budget > 1 && exam_search (c, budget)));
+}
+
+/* The body of the recursive search for C within BUDGET.
+   We've already failed exam_simple.  */
+
+bool
+genimm_ppc::exam_search (HOST_WIDE_INT c, int budget)
+{
+  const int sub_budget = budget - 1;
+  HOST_WIDE_INT test;
+
+  /* Simple arithmetic on the low 32-bits.  */
+  test = c & 0xffff;
+  if (test != 0)
+    {
+      if (exam_sub (c ^ test, sub_budget))
+	{
+	  opN (IOR, test); /* ORI */
+	  return true;
+	}
+      /* Note that it doesn't help to try ORIS 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 = c & 0xffff0000u;
+      if (test != 0 && exam_sub (c ^ test, sub_budget))
+	{
+	  opN (IOR, test); /* ORIS */
+	  return true;
+	}
+    }
+
+  /* Shift the constant left.  */
+  test = HOST_WIDE_INT_UC (0xffffffff00000000);
+  if ((c & test) == c && exam_sub (c >> 32, sub_budget))
+    {
+      opN (ASHIFT, 32); /* SLDI */
+      return true;
+    }
+
+  return false;
+}
+
+/* Only handle full 64-bit constants requiring 5 insns.  */
+
+void
+genimm_ppc::exam_full (HOST_WIDE_INT c)
+{
+  bool ok = exam_simple (c >> 32, DImode, 2);
+  gcc_assert (ok);
+
+  opN (ASHIFT, 32);
+  if (c & 0xffff0000u)
+    opN (IOR, c & 0xffff0000u);
+  if (c & 0xffff)
+    opN (IOR, c & 0xffff);
+}
+
+/* Emit insns for the generated recipe.  */
+
+void
+genimm_ppc::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;
+
+  gcc_checking_assert (n >= 1 && n <= max_cost);
+  gcc_checking_assert (code[0] == SET);
+
+  for (i = 0; i < n; ++i)
+    {
+      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)
+	{
+	case SET:
+	  x = op2;
+	  break;
+	case PLUS:
+	case IOR:
+	case ASHIFT:
+	  x = gen_rtx_fmt_ee (r, mode, op1, op2);
+	  break;
+	default:
+	  gcc_unreachable ();
+	}
+
+      insn = emit_insn (gen_rtx_SET (sub, x));
+      op1 = sub;
+    }
+
+  if (n > 1)
+    set_unique_reg_note (insn, REG_EQUAL, GEN_INT (value));
+}
+
+} // anon namespace
+
+/* Return the number of instructions it takes to form a constant in an
+   integer register.  */
+
+static int
+num_insns_constant_wide (HOST_WIDE_INT c)
+{
+  machine_mode mode = TARGET_POWERPC64 ? DImode : SImode;
+  genimm_ppc data = genimm_hash<genimm_ppc>::hash (c, mode);
+  return data.cost;
+}
+
+static void
+rs6000_emit_set_const_1 (rtx dest, HOST_WIDE_INT c)
+{
+  machine_mode mode = GET_MODE (dest);
+  genimm_ppc data = genimm_hash<genimm_ppc>::hash (c, mode);
+  data.generate (dest, mode);
+}
+
 /* Output insns to set DEST equal to the constant SOURCE as a series of
    lis, ori and shl instructions and return TRUE.  */
 
@@ -8093,12 +8253,8 @@  bool
 rs6000_emit_set_const (rtx dest, rtx source)
 {
   machine_mode mode = GET_MODE (dest);
-  rtx temp, set;
-  rtx_insn *insn;
-  HOST_WIDE_INT c;
+  HOST_WIDE_INT c = INTVAL (source);
 
-  gcc_checking_assert (CONST_INT_P (source));
-  c = INTVAL (source);
   switch (mode)
     {
     case QImode:
@@ -8107,138 +8263,26 @@  rs6000_emit_set_const (rtx dest, rtx source)
       return true;
 
     case SImode:
-      temp = !can_create_pseudo_p () ? dest : gen_reg_rtx (SImode);
-
-      emit_insn (gen_rtx_SET (copy_rtx (temp),
-			      GEN_INT (c & ~(HOST_WIDE_INT) 0xffff)));
-      emit_insn (gen_rtx_SET (dest,
-			      gen_rtx_IOR (SImode, copy_rtx (temp),
-					   GEN_INT (c & 0xffff))));
       break;
-
     case DImode:
       if (!TARGET_POWERPC64)
 	{
-	  rtx hi, lo;
-
-	  hi = operand_subword_force (copy_rtx (dest), WORDS_BIG_ENDIAN == 0,
-				      DImode);
-	  lo = operand_subword_force (dest, WORDS_BIG_ENDIAN != 0,
-				      DImode);
-	  emit_move_insn (hi, GEN_INT (c >> 32));
-	  c = ((c & 0xffffffff) ^ 0x80000000) - 0x80000000;
-	  emit_move_insn (lo, GEN_INT (c));
+	  rtx hi = operand_subword_force (dest, WORDS_BIG_ENDIAN == 0, DImode);
+	  rtx lo = operand_subword_force (dest, WORDS_BIG_ENDIAN != 0, DImode);
+	  rs6000_emit_set_const_1 (hi, trunc_int_for_mode (c >> 32, SImode));
+	  rs6000_emit_set_const_1 (lo, trunc_int_for_mode (c, SImode));
+	  return true;
 	}
-      else
-	rs6000_emit_set_long_const (dest, c);
       break;
 
     default:
       gcc_unreachable ();
     }
 
-  insn = get_last_insn ();
-  set = single_set (insn);
-  if (! CONSTANT_P (SET_SRC (set)))
-    set_unique_reg_note (insn, REG_EQUAL, GEN_INT (c));
-
+  rs6000_emit_set_const_1 (dest, c);
   return true;
 }
 
-/* Subroutine of rs6000_emit_set_const, handling PowerPC64 DImode.
-   Output insns to set DEST equal to the constant C as a series of
-   lis, ori and shl instructions.  */
-
-static void
-rs6000_emit_set_long_const (rtx dest, HOST_WIDE_INT c)
-{
-  rtx temp;
-  HOST_WIDE_INT ud1, ud2, ud3, ud4;
-
-  ud1 = c & 0xffff;
-  c = c >> 16;
-  ud2 = c & 0xffff;
-  c = c >> 16;
-  ud3 = c & 0xffff;
-  c = c >> 16;
-  ud4 = c & 0xffff;
-
-  if ((ud4 == 0xffff && ud3 == 0xffff && ud2 == 0xffff && (ud1 & 0x8000))
-      || (ud4 == 0 && ud3 == 0 && ud2 == 0 && ! (ud1 & 0x8000)))
-    emit_move_insn (dest, GEN_INT ((ud1 ^ 0x8000) - 0x8000));
-
-  else if ((ud4 == 0xffff && ud3 == 0xffff && (ud2 & 0x8000))
-	   || (ud4 == 0 && ud3 == 0 && ! (ud2 & 0x8000)))
-    {
-      temp = !can_create_pseudo_p () ? dest : gen_reg_rtx (DImode);
-
-      emit_move_insn (ud1 != 0 ? copy_rtx (temp) : dest,
-		      GEN_INT (((ud2 << 16) ^ 0x80000000) - 0x80000000));
-      if (ud1 != 0)
-	emit_move_insn (dest,
-			gen_rtx_IOR (DImode, copy_rtx (temp),
-				     GEN_INT (ud1)));
-    }
-  else if (ud3 == 0 && ud4 == 0)
-    {
-      temp = !can_create_pseudo_p () ? dest : gen_reg_rtx (DImode);
-
-      gcc_assert (ud2 & 0x8000);
-      emit_move_insn (copy_rtx (temp),
-		      GEN_INT (((ud2 << 16) ^ 0x80000000) - 0x80000000));
-      if (ud1 != 0)
-	emit_move_insn (copy_rtx (temp),
-			gen_rtx_IOR (DImode, copy_rtx (temp),
-				     GEN_INT (ud1)));
-      emit_move_insn (dest,
-		      gen_rtx_ZERO_EXTEND (DImode,
-					   gen_lowpart (SImode,
-							copy_rtx (temp))));
-    }
-  else if ((ud4 == 0xffff && (ud3 & 0x8000))
-	   || (ud4 == 0 && ! (ud3 & 0x8000)))
-    {
-      temp = !can_create_pseudo_p () ? dest : gen_reg_rtx (DImode);
-
-      emit_move_insn (copy_rtx (temp),
-		      GEN_INT (((ud3 << 16) ^ 0x80000000) - 0x80000000));
-      if (ud2 != 0)
-	emit_move_insn (copy_rtx (temp),
-			gen_rtx_IOR (DImode, copy_rtx (temp),
-				     GEN_INT (ud2)));
-      emit_move_insn (ud1 != 0 ? copy_rtx (temp) : dest,
-		      gen_rtx_ASHIFT (DImode, copy_rtx (temp),
-				      GEN_INT (16)));
-      if (ud1 != 0)
-	emit_move_insn (dest,
-			gen_rtx_IOR (DImode, copy_rtx (temp),
-				     GEN_INT (ud1)));
-    }
-  else
-    {
-      temp = !can_create_pseudo_p () ? dest : gen_reg_rtx (DImode);
-
-      emit_move_insn (copy_rtx (temp),
-		      GEN_INT (((ud4 << 16) ^ 0x80000000) - 0x80000000));
-      if (ud3 != 0)
-	emit_move_insn (copy_rtx (temp),
-			gen_rtx_IOR (DImode, copy_rtx (temp),
-				     GEN_INT (ud3)));
-
-      emit_move_insn (ud2 != 0 || ud1 != 0 ? copy_rtx (temp) : dest,
-		      gen_rtx_ASHIFT (DImode, copy_rtx (temp),
-				      GEN_INT (32)));
-      if (ud2 != 0)
-	emit_move_insn (ud1 != 0 ? copy_rtx (temp) : dest,
-			gen_rtx_IOR (DImode, copy_rtx (temp),
-				     GEN_INT (ud2 << 16)));
-      if (ud1 != 0)
-	emit_move_insn (dest,
-			gen_rtx_IOR (DImode, copy_rtx (temp),
-				     GEN_INT (ud1)));
-    }
-}
-
 /* Helper for the following.  Get rid of [r+r] memory refs
    in cases where it won't work (TImode, TFmode, TDmode, PTImode).  */
 
diff --git a/gcc/genimm-hash.h b/gcc/genimm-hash.h
new file mode 100644
index 0000000..73752e2
--- /dev/null
+++ b/gcc/genimm-hash.h
@@ -0,0 +1,122 @@ 
+/* Hash and memoize constant integer construction.
+   Copyright (C) 2015 Free Software Foundation, Inc.
+
+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/>.  */
+
+#ifndef GENIMM_HASH_H
+#define GENIMM_HASH_H
+
+/* The base class for the genimm infrastructure.  */
+
+template <typename CODETYPE, int MAXCOST>
+struct genimm_base
+{
+  static const int max_cost = MAXCOST;
+
+  /* The value we're generating.  */
+  const HOST_WIDE_INT value;
+
+  /* The sequence of operations required to generate the constant.
+     The code[0] will always be SET, with r[0] = GEN_INT (op[0])
+     being the initial value.  Subsequent intermediate results
+     r[i] are a function of code[i], op[i] and r[i-1].  */
+  HOST_WIDE_INT op[max_cost];
+  CODETYPE code[max_cost];
+
+  /* The number of operations required to generate the constant.  */
+  int cost;
+
+  genimm_base(HOST_WIDE_INT c) : value(c), cost(0) { }
+};
+
+/* The "middle" class in the hierarchy should be provided by the backend.
+   It should provide the following functions:
+
+   -- Recognize "simple" values that shouldn't be hashed, but normally
+      also used as the base of any recursive search.  Returns true if
+      the constant can be generated within BUDGET.
+   bool exam_simple (HOST_WIDE_INT c, machine_mode mode, int budget);
+
+   -- The maximum number of insns matched by exam_simple.
+   static const int max_simple;
+
+   -- The full search for a word_mode constant.  Normally recursive,
+      with decreasing values for BUDGET.  Normally written to assume
+      that the constant has failed to match exam_simple.
+   bool exam_search (HOST_WIDE_INT c, int budget);
+
+   -- The fallback generation for the most complex word_mode constants.
+      The receipe built will be the full MAX_COST insns, as we will
+      already have shown that the constant can't be built with fewer.
+   void exam_full (HOST_WIDE_INT c);
+
+   -- Generate code via the stored recipe, placing the final result in DEST.
+   void generate (rtx dest, machine_mode mode) const;
+
+   This backend class is the template argument to genimm_hash.
+*/
+
+template <typename BACKEND>
+class genimm_hash
+{
+  struct hasher : free_ptr_hash<BACKEND>
+  {
+    typedef HOST_WIDE_INT compare_type;
+    static hashval_t hash (BACKEND *i) { return i->value; }
+    static bool equal (BACKEND *a, compare_type b) { return a->value == b; }
+  };
+
+ public:
+  static BACKEND hash (HOST_WIDE_INT c, machine_mode mode);
+};
+
+template <typename BACKEND>
+BACKEND
+genimm_hash<BACKEND>::hash (HOST_WIDE_INT c, machine_mode mode)
+{
+  BACKEND data (c);
+
+  /* Don't hash simple constants.  Just return them immediately.  */
+  if (!data.exam_simple (c, mode, BACKEND::max_simple))
+    {
+      static hash_table<hasher> *htab;
+      if (htab == NULL)
+	htab = new hash_table<hasher> (128);
+
+      BACKEND **slot = htab->find_slot_with_hash (c, c, INSERT);
+      BACKEND *save = *slot;
+
+      /* Return a previously stored result.  */
+      if (save != NULL)
+	return *save;
+
+      /* Search for a solution to C with increasing budget...  */
+      bool ok = false;
+      for (int budget = 2; !ok && budget < BACKEND::max_cost - 1; ++budget)
+	ok = data.exam_search (c, budget);
+
+      /* ... otherwise, we know that C requires the maximum budget.  */
+      if (!ok)
+	data.exam_full (c);
+
+      *slot = new BACKEND (data);
+    }
+
+  return data;
+}
+
+#endif /* GENIMM_HASH_H */