Implement more two insn constants.
PR 94393
* config/rs6000/rs6000.c (rotate_and_mask_constant): New function.
(num_insns_constant_multi, rs6000_emit_set_long_const): Use it here.
diff --git a/gcc/config/rs6000/rs6000.c b/gcc/config/rs6000/rs6000.c
index 86c90c4d756..1848cb57ef8 100644
--- a/gcc/config/rs6000/rs6000.c
+++ b/gcc/config/rs6000/rs6000.c
@@ -1112,6 +1112,8 @@ 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 (combined_fn, tree, tree);
static void rs6000_emit_set_long_const (rtx, HOST_WIDE_INT);
+static bool rotate_and_mask_constant (unsigned HOST_WIDE_INT, HOST_WIDE_INT *,
+ int *, unsigned HOST_WIDE_INT *);
static int rs6000_memory_move_cost (machine_mode, reg_class_t, bool);
static bool rs6000_debug_rtx_costs (rtx, machine_mode, int, int, int *, bool);
static int rs6000_debug_address_cost (rtx, machine_mode, addr_space_t,
@@ -5789,7 +5791,8 @@ num_insns_constant_multi (HOST_WIDE_INT value,
machine_mode mode)
/* We won't get more than 2 from num_insns_constant_gpr
except when TARGET_POWERPC64 and mode is DImode or
wider, so the register mode must be DImode. */
- && rs6000_is_valid_and_mask (GEN_INT (low), DImode))
+ && (rs6000_is_valid_and_mask (GEN_INT (low), DImode)
+ || rotate_and_mask_constant (low, NULL, NULL, NULL)))
insns = 2;
total += insns;
/* If BITS_PER_WORD is the number of bits in HOST_WIDE_INT, doing
@@ -9420,6 +9423,82 @@ rs6000_emit_set_const (rtx dest, rtx source)
return true;
}
+/* Detect cases where a constant can be formed by li; rldicl, li; rldicr,
+ or lis; rldicl. */
+
+static bool
+rotate_and_mask_constant (unsigned HOST_WIDE_INT c,
+ HOST_WIDE_INT *val, int *shift,
+ unsigned HOST_WIDE_INT *mask)
+{
+ /* We know C can't be formed by lis,addi so that puts constraints
+ on the max leading zeros. lead_zeros won't be larger than
+ HOST_BITS_PER_WIDE_INT - 31. */
+ int lead_zeros = wi::clz (c);
+ int non_zero = HOST_BITS_PER_WIDE_INT - lead_zeros;
+ /* 00...01xxxxxxxxxxxxxx0..00 (up to 14 x's, any number of leading
+ and trailing 0's) can be implemented as a li, rldicl. */
+ if ((c & ~(HOST_WIDE_INT_UC (0x7fff) << (non_zero - 15))) == 0)
+ {
+ /* eg. c = 1100 0000 0000 ... 0000
+ -> val = 0x3000, shift = 49, mask = -1ull. */
+ if (val)
+ {
+ *val = c >> (non_zero - 15);
+ *shift = non_zero - 15;
+ *mask = HOST_WIDE_INT_M1U;
+ }
+ return true;
+ }
+ /* 00...01xxxxxxxxxxxxxxx1..11 (up to 15 x's, any number of leading
+ 0's and trailing 1's) can be implemented as a li, rldicl. */
+ if ((c | (HOST_WIDE_INT_M1U << (non_zero - 16))) == HOST_WIDE_INT_M1U)
+ {
+ /* eg. c = 0000 1011 1111 1111 ... 1111
+ -> val = sext(0xbfff), shift = 44, mask = 0x0fffffffffffffff. */
+ if (val)
+ {
+ *val = (((c >> (non_zero - 16)) & 0xffff) ^ 0x8000) - 0x8000;
+ *shift = non_zero - 16;
+ *mask = HOST_WIDE_INT_M1U >> lead_zeros;
+ }
+ return true;
+ }
+ /* 00...01xxxxxxxxxxxxxxx00..01..11 (up to 15 x's followed by 16 0's,
+ any number of leading 0's and trailing 1's) can be implemented as
+ lis, rldicl. */
+ if (non_zero >= 32
+ && (c & ((HOST_WIDE_INT_1U << (non_zero - 16))
+ - (HOST_WIDE_INT_1U << (non_zero - 32)))) == 0
+ && (c | (HOST_WIDE_INT_M1U << (non_zero - 32))) == HOST_WIDE_INT_M1U)
+ {
+ if (val)
+ {
+ *val = (((c >> (non_zero - 32)) & 0xffffffff)
+ ^ 0x80000000) - 0x80000000;
+ *shift = non_zero - 32;
+ *mask = HOST_WIDE_INT_M1U >> lead_zeros;
+ }
+ return true;
+ }
+ /* 11..1xxxxxxxxxxxxxxx0..0 (up to 15 x's, any number of leading 1's
+ and trailing 0's) can be implemented as a li, rldicr. */
+ int trail_zeros = wi::ctz (c);
+ if (trail_zeros >= 48
+ || ((c | ((HOST_WIDE_INT_1U << (trail_zeros + 15)) - 1))
+ == HOST_WIDE_INT_M1U))
+ {
+ if (val)
+ {
+ *val = (((c >> trail_zeros) & 0xffff) ^ 0x8000) - 0x8000;
+ *shift = trail_zeros;
+ *mask = HOST_WIDE_INT_M1U << trail_zeros;
+ }
+ return true;
+ }
+ return false;
+}
+
/* 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. */
@@ -9429,6 +9508,9 @@ rs6000_emit_set_long_const (rtx dest, HOST_WIDE_INT c)
{
rtx temp;
HOST_WIDE_INT ud1, ud2, ud3, ud4;
+ HOST_WIDE_INT val = c;
+ int shift;
+ unsigned HOST_WIDE_INT mask;
ud1 = c & 0xffff;
c = c >> 16;
@@ -9454,6 +9536,15 @@ rs6000_emit_set_long_const (rtx dest, HOST_WIDE_INT c)
gen_rtx_IOR (DImode, copy_rtx (temp),
GEN_INT (ud1)));
}
+ else if (rotate_and_mask_constant (val, &val, &shift, &mask))
+ {
+ temp = !can_create_pseudo_p () ? dest : gen_reg_rtx (DImode);
+ emit_move_insn (temp, GEN_INT (val));
+ rtx x = gen_rtx_ROTATE (DImode, copy_rtx (temp), GEN_INT (shift));
+ if (mask != HOST_WIDE_INT_M1U)
+ x = gen_rtx_AND (DImode, x, GEN_INT (mask));
+ emit_move_insn (dest, x);
+ }
else if (ud3 == 0 && ud4 == 0)
{
temp = !can_create_pseudo_p () ? dest : gen_reg_rtx (DImode);