@@ -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). */
new file mode 100644
@@ -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 */