@@ -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;