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