From 8f1a70a4fbcc6441c70da60d4ef6db1e5635e18a Mon Sep 17 00:00:00 2001 From: Jiufu Guo Date: Tue, 10 Jan 2023 20:52:33 +0800 Subject: [PATCH] rs6000: build constant via li/lis;rldicl/rldicr If a constant is possible left/right cleaned on a rotated value from a negative value of "li/lis". Then, using "li/lis ; rldicl/rldicr" to build the constant. gcc/ChangeLog: * config/rs6000/rs6000.cc (can_be_built_by_li_lis_and_rldicl): New function. (can_be_built_by_li_lis_and_rldicr): New function. (rs6000_emit_set_long_const): Call can_be_built_by_li_lis_and_rldicr and can_be_built_by_li_lis_and_rldicl. gcc/testsuite/ChangeLog: * gcc.target/powerpc/const-build.c: Add more tests. --- gcc/config/rs6000/rs6000.cc | 61 ++++++++++++++++++- .../gcc.target/powerpc/const-build.c | 46 +++++++++++++- 2 files changed, 105 insertions(+), 2 deletions(-) diff --git a/gcc/config/rs6000/rs6000.cc b/gcc/config/rs6000/rs6000.cc index 65690526ec77..68b4a6fe4532 100644 --- a/gcc/config/rs6000/rs6000.cc +++ b/gcc/config/rs6000/rs6000.cc @@ -10356,6 +10356,61 @@ can_be_built_by_li_lis_and_rotldi (HOST_WIDE_INT c, int *shift, return false; } +/* Check if value C can be built by 2 instructions: one is 'li or lis', + another is rldicl. + + If so, *SHIFT is set to the shift operand of rldicl, and *MASK is set to + the mask operand of rldicl, and return true. + Return false otherwise. */ + +static bool +can_be_built_by_li_lis_and_rldicl (HOST_WIDE_INT c, int *shift, + HOST_WIDE_INT *mask) +{ + /* Leading zeros may be cleaned by rldicl with a mask. Change leading zeros + to ones and then recheck it. */ + int lz = clz_hwi (c); + HOST_WIDE_INT unmask_c + = c | (HOST_WIDE_INT_M1U << (HOST_BITS_PER_WIDE_INT - lz)); + int n; + if (can_be_rotated_to_lowbits (~unmask_c, 15, &n) + || can_be_rotated_to_negative_lis (unmask_c, &n)) + { + *mask = HOST_WIDE_INT_M1U >> lz; + *shift = n == 0 ? 0 : HOST_BITS_PER_WIDE_INT - n; + return true; + } + + return false; +} + +/* Check if value C can be built by 2 instructions: one is 'li or lis', + another is rldicr. + + If so, *SHIFT is set to the shift operand of rldicr, and *MASK is set to + the mask operand of rldicr, and return true. + Return false otherwise. */ + +static bool +can_be_built_by_li_lis_and_rldicr (HOST_WIDE_INT c, int *shift, + HOST_WIDE_INT *mask) +{ + /* Tailing zeros may be cleaned by rldicr with a mask. Change tailing zeros + to ones and then recheck it. */ + int tz = ctz_hwi (c); + HOST_WIDE_INT unmask_c = c | ((HOST_WIDE_INT_1U << tz) - 1); + int n; + if (can_be_rotated_to_lowbits (~unmask_c, 15, &n) + || can_be_rotated_to_negative_lis (unmask_c, &n)) + { + *mask = HOST_WIDE_INT_M1U << tz; + *shift = HOST_BITS_PER_WIDE_INT - n; + 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. */ @@ -10402,7 +10457,9 @@ rs6000_emit_set_long_const (rtx dest, HOST_WIDE_INT c) emit_move_insn (dest, gen_rtx_XOR (DImode, temp, GEN_INT ((ud2 ^ 0xffff) << 16))); } - else if (can_be_built_by_li_lis_and_rotldi (c, &shift, &mask)) + else if (can_be_built_by_li_lis_and_rotldi (c, &shift, &mask) + || can_be_built_by_li_lis_and_rldicl (c, &shift, &mask) + || can_be_built_by_li_lis_and_rldicr (c, &shift, &mask)) { temp = !can_create_pseudo_p () ? dest : gen_reg_rtx (DImode); unsigned HOST_WIDE_INT imm = (c | ~mask); @@ -10411,6 +10468,8 @@ rs6000_emit_set_long_const (rtx dest, HOST_WIDE_INT c) emit_move_insn (temp, GEN_INT (imm)); if (shift != 0) temp = gen_rtx_ROTATE (DImode, temp, GEN_INT (shift)); + if (mask != HOST_WIDE_INT_M1) + temp = gen_rtx_AND (DImode, temp, GEN_INT (mask)); emit_move_insn (dest, temp); } else if (ud3 == 0 && ud4 == 0) diff --git a/gcc/testsuite/gcc.target/powerpc/const-build.c b/gcc/testsuite/gcc.target/powerpc/const-build.c index 63e0ed40a1c1..9090bc4e6c95 100644 --- a/gcc/testsuite/gcc.target/powerpc/const-build.c +++ b/gcc/testsuite/gcc.target/powerpc/const-build.c @@ -3,7 +3,7 @@ /* { dg-require-effective-target has_arch_ppc64 } */ /* Verify that two instructions are successfully used to build constants. - One insn is li or lis, another is rotate: rldicl. */ + One insn is li or lis, another is rotate: rldicl or rldicr. */ #define NOIPA __attribute__ ((noipa)) @@ -49,6 +49,42 @@ lis_rotldi_6 (void) return 0x5310000ffffffff8LL; } +long long NOIPA +li_rldicl_7 (void) +{ + return 0x3ffffffa1LL; +} + +long long NOIPA +li_rldicl_8 (void) +{ + return 0xff8531ffffffffLL; +} + +long long NOIPA +lis_rldicl_9 (void) +{ + return 0x00ff85310000ffffLL; +} + +long long NOIPA +li_rldicr_10 (void) +{ + return 0xffff8531fff00000LL; +} + +long long NOIPA +li_rldicr_11 (void) +{ + return 0x21fffffffff00000LL; +} + +long long NOIPA +lis_rldicr_12 (void) +{ + return 0x5310000ffffffff0LL; +} + struct fun arr[] = { {li_rotldi_1, 0x7531000000000LL}, {li_rotldi_2, 0x2100000000000064LL}, @@ -56,9 +92,17 @@ struct fun arr[] = { {li_rotldi_4, 0x21ffffffffffff94LL}, {lis_rotldi_5, 0xffff85310000ffffLL}, {lis_rotldi_6, 0x5310000ffffffff8LL}, + {li_rldicl_7, 0x3ffffffa1LL}, + {li_rldicl_8, 0xff8531ffffffffLL}, + {lis_rldicl_9, 0x00ff85310000ffffLL}, + {li_rldicr_10, 0xffff8531fff00000LL}, + {li_rldicr_11, 0x21fffffffff00000LL}, + {lis_rldicr_12, 0x5310000ffffffff0LL}, }; /* { dg-final { scan-assembler-times {\mrotldi\M} 6 } } */ +/* { dg-final { scan-assembler-times {\mrldicl\M} 3 } } */ +/* { dg-final { scan-assembler-times {\mrldicr\M} 3 } } */ int main () -- 2.43.5