Optimize the generated code for the bit-field extract and insert patterns: - Use bit-set and bit-clear instructions for 1-bit fields. - Expand to SImode operations instead of relying on the default expansion to word (QI) mode.
gcc/ChangeLog: * config/pru/pru.md (extzv<mode>): Make it an expand pattern, handle efficiently zero-positioned bit-fields. (insv<mode>): New expand pattern. gcc/testsuite/ChangeLog: * gcc.target/pru/ashiftrt.c: Minor update due to new (but equivalent) generated code sequence. * gcc.target/pru/extzv-1.c: New test. * gcc.target/pru/extzv-2.c: New test. * gcc.target/pru/extzv-3.c: New test. * gcc.target/pru/insv-1.c: New test. * gcc.target/pru/insv-2.c: New test. * gcc.target/pru/insv-3.c: New test. * gcc.target/pru/insv-4.c: New test. Signed-off-by: Dimitar Dimitrov <dimi...@dinux.eu> --- gcc/config/pru/pru.md | 104 ++++++++++++++++++++++-- gcc/testsuite/gcc.target/pru/ashiftrt.c | 2 +- gcc/testsuite/gcc.target/pru/extzv-1.c | 14 ++++ gcc/testsuite/gcc.target/pru/extzv-2.c | 15 ++++ gcc/testsuite/gcc.target/pru/extzv-3.c | 13 +++ gcc/testsuite/gcc.target/pru/insv-1.c | 14 ++++ gcc/testsuite/gcc.target/pru/insv-2.c | 14 ++++ gcc/testsuite/gcc.target/pru/insv-3.c | 14 ++++ gcc/testsuite/gcc.target/pru/insv-4.c | 14 ++++ 9 files changed, 194 insertions(+), 10 deletions(-) create mode 100644 gcc/testsuite/gcc.target/pru/extzv-1.c create mode 100644 gcc/testsuite/gcc.target/pru/extzv-2.c create mode 100644 gcc/testsuite/gcc.target/pru/extzv-3.c create mode 100644 gcc/testsuite/gcc.target/pru/insv-1.c create mode 100644 gcc/testsuite/gcc.target/pru/insv-2.c create mode 100644 gcc/testsuite/gcc.target/pru/insv-3.c create mode 100644 gcc/testsuite/gcc.target/pru/insv-4.c diff --git a/gcc/config/pru/pru.md b/gcc/config/pru/pru.md index 0123952aa9e..2ceea2e7b1c 100644 --- a/gcc/config/pru/pru.md +++ b/gcc/config/pru/pru.md @@ -486,22 +486,108 @@ (define_expand "extend<EQS0:mode><EQDHIDI:mode>2" }) ;; Bit extraction -;; We define it solely to allow combine to choose SImode +;; One reason to define it is to allow combine to choose SImode ;; for word mode when trying to match our cbranch_qbbx_* insn. ;; ;; Check how combine.cc:make_extraction() uses ;; get_best_reg_extraction_insn() to select the op size. -(define_insn "extzv<mode>" - [(set (match_operand:QISI 0 "register_operand" "=r") +(define_expand "extzv<mode>" + [(set (match_operand:QISI 0 "register_operand") (zero_extract:QISI - (match_operand:QISI 1 "register_operand" "r") - (match_operand:QISI 2 "const_int_operand" "i") - (match_operand:QISI 3 "const_int_operand" "i")))] + (match_operand:QISI 1 "register_operand") + (match_operand:QISI 2 "const_int_operand") + (match_operand:QISI 3 "const_int_operand")))] "" - "lsl\\t%0, %1, (%S0 * 8 - %2 - %3)\;lsr\\t%0, %0, (%S0 * 8 - %2)" - [(set_attr "type" "complex") - (set_attr "length" "8")]) +{ + const int nbits = INTVAL (operands[2]); + const int bitpos = INTVAL (operands[3]); + const int trailing_bits = GET_MODE_BITSIZE (<MODE>mode) - nbits - bitpos; + + if (bitpos == 0 && nbits <= 7) + { + emit_insn (gen_and<mode>3 (operands[0], + operands[1], + gen_int_mode ((HOST_WIDE_INT_1U << nbits) - 1, + <MODE>mode))); + DONE; + } + + rtx src = operands[1]; + if (trailing_bits != 0) + { + emit_insn (gen_ashl<mode>3 (operands[0], + operands[1], + GEN_INT (trailing_bits))); + src = operands[0]; + } + emit_insn (gen_lshr<mode>3 (operands[0], + src, + GEN_INT (trailing_bits + bitpos))); + DONE; +}) + +;; Bit-field insert. +(define_expand "insv<mode>" + [(set (zero_extract:QISI + (match_operand:QISI 0 "register_operand") + (match_operand:QISI 1 "const_int_operand") + (match_operand:QISI 2 "const_int_operand")) + (match_operand:QISI 3 "reg_or_ubyte_operand"))] + "" +{ + const int nbits = INTVAL (operands[1]); + const int bitpos = INTVAL (operands[2]); + if (nbits == 1) + { + rtx j; + rtx src = gen_reg_rtx (<MODE>mode); + rtx dst = operands[0]; + + emit_move_insn (src, operands[3]); + + emit_insn (gen_and<mode>3 (dst, + dst, + gen_int_mode (~(HOST_WIDE_INT_1U << bitpos), + <MODE>mode))); + + rtx_code_label *skip_set_label = gen_label_rtx (); + j = emit_jump_insn (gen_cbranch_qbbx_eq<mode><mode><mode>4 ( + src, + GEN_INT (0), + skip_set_label)); + JUMP_LABEL (j) = skip_set_label; + LABEL_NUSES (skip_set_label)++; + + emit_insn (gen_ior<mode>3 (dst, + dst, + gen_int_mode (HOST_WIDE_INT_1U << bitpos, + <MODE>mode))); + emit_label (skip_set_label); + + DONE; + } + + /* Explicitly expand in order to avoid using word_mode for PRU, and instead + use SI and HI modes as applicable. */ + rtx dst = operands[0]; + rtx src = gen_reg_rtx (<MODE>mode); + emit_insn (gen_and<mode>3 (src, + force_reg (<MODE>mode, operands[3]), + gen_int_mode ((HOST_WIDE_INT_1U << nbits) - 1, + <MODE>mode))); + if (bitpos > 0) + emit_insn (gen_ashl<mode>3 (src, src, gen_int_mode (bitpos, <MODE>mode))); + + HOST_WIDE_INT vmask = ~(((HOST_WIDE_INT_1U << nbits) - 1) << bitpos); + emit_insn (gen_and<mode>3 (dst, + dst, + gen_int_mode (vmask, <MODE>mode))); + + emit_insn (gen_ior<mode>3 (dst, dst, src)); + + DONE; +}) ;; Arithmetic Operations diff --git a/gcc/testsuite/gcc.target/pru/ashiftrt.c b/gcc/testsuite/gcc.target/pru/ashiftrt.c index ee8d55d60e6..fb8e4759a0e 100644 --- a/gcc/testsuite/gcc.target/pru/ashiftrt.c +++ b/gcc/testsuite/gcc.target/pru/ashiftrt.c @@ -8,6 +8,6 @@ extern void func2(unsigned char); void test(unsigned char v) { - /* { dg-final { scan-assembler "lsl\tr14.b0, r14.b0, .\+\n\tlsr\tr14.b0, r14.b0" } } */ + /* { dg-final { scan-assembler "lsr\tr14(.b0)?, r14.b0, .\+\n\tand\tr14.b0, r14.b0" } } */ func2((v & 2) ? 1 : 0); } diff --git a/gcc/testsuite/gcc.target/pru/extzv-1.c b/gcc/testsuite/gcc.target/pru/extzv-1.c new file mode 100644 index 00000000000..573ded99830 --- /dev/null +++ b/gcc/testsuite/gcc.target/pru/extzv-1.c @@ -0,0 +1,14 @@ +/* { dg-do assemble } */ +/* { dg-options "-Os" } */ +/* { dg-final { object-size text <= 12 } } */ + +struct S { + unsigned int a : 5; + unsigned int b : 1; + unsigned int c : 1; +}; + +unsigned int test(struct S s) +{ + return s.a; +} diff --git a/gcc/testsuite/gcc.target/pru/extzv-2.c b/gcc/testsuite/gcc.target/pru/extzv-2.c new file mode 100644 index 00000000000..e34ba138f16 --- /dev/null +++ b/gcc/testsuite/gcc.target/pru/extzv-2.c @@ -0,0 +1,15 @@ +/* { dg-do assemble } */ +/* { dg-options "-Os" } */ +/* { dg-final { object-size text <= 12 } } */ + +struct S { + unsigned int a : 5; + unsigned int b : 1; + unsigned int c : 24; + unsigned int d : 2; +}; + +unsigned int test(struct S s) +{ + return s.d; +} diff --git a/gcc/testsuite/gcc.target/pru/extzv-3.c b/gcc/testsuite/gcc.target/pru/extzv-3.c new file mode 100644 index 00000000000..66f4f376885 --- /dev/null +++ b/gcc/testsuite/gcc.target/pru/extzv-3.c @@ -0,0 +1,13 @@ +/* { dg-do assemble } */ +/* { dg-options "-Os" } */ +/* { dg-final { object-size text <= 16 } } */ + +struct S { + unsigned int a : 9; + unsigned int b : 4; +}; + +unsigned int test(struct S s) +{ + return s.b; +} diff --git a/gcc/testsuite/gcc.target/pru/insv-1.c b/gcc/testsuite/gcc.target/pru/insv-1.c new file mode 100644 index 00000000000..50e29a1b818 --- /dev/null +++ b/gcc/testsuite/gcc.target/pru/insv-1.c @@ -0,0 +1,14 @@ +/* { dg-do assemble } */ +/* { dg-options "-Os" } */ +/* { dg-final { object-size text <= 16 } } */ + +struct S { + unsigned int a : 5; + unsigned int b : 1; + unsigned int c : 1; +}; + +void test(struct S *s) +{ + s->b = 1; +} diff --git a/gcc/testsuite/gcc.target/pru/insv-2.c b/gcc/testsuite/gcc.target/pru/insv-2.c new file mode 100644 index 00000000000..50272b713e7 --- /dev/null +++ b/gcc/testsuite/gcc.target/pru/insv-2.c @@ -0,0 +1,14 @@ +/* { dg-do assemble } */ +/* { dg-options "-Os" } */ +/* { dg-final { object-size text <= 16 } } */ + +struct S { + unsigned int a : 5; + unsigned int b : 1; + unsigned int c : 1; +}; + +void test(struct S *s) +{ + s->b = 0; +} diff --git a/gcc/testsuite/gcc.target/pru/insv-3.c b/gcc/testsuite/gcc.target/pru/insv-3.c new file mode 100644 index 00000000000..5ff0feb2ca1 --- /dev/null +++ b/gcc/testsuite/gcc.target/pru/insv-3.c @@ -0,0 +1,14 @@ +/* { dg-do assemble } */ +/* { dg-options "-Os" } */ +/* { dg-final { object-size text <= 24 } } */ + +struct S { + unsigned int a : 5; + unsigned int b : 1; + unsigned int c : 1; +}; + +void test(struct S *s, unsigned int val) +{ + s->b = val; +} diff --git a/gcc/testsuite/gcc.target/pru/insv-4.c b/gcc/testsuite/gcc.target/pru/insv-4.c new file mode 100644 index 00000000000..4eaa733c560 --- /dev/null +++ b/gcc/testsuite/gcc.target/pru/insv-4.c @@ -0,0 +1,14 @@ +/* { dg-do assemble } */ +/* { dg-options "-Os" } */ +/* { dg-final { object-size text <= 28 } } */ + +struct S { + unsigned int a : 3; + unsigned int b : 3; + unsigned int c : 3; +}; + +void test(struct S *s, unsigned int val) +{ + s->b = val; +} -- 2.45.0