RISC-V: Optimize branches testing a bit-range or a shifted immediate
Checks
Commit Message
gcc/ChangeLog:
* config/riscv/predicates.md (shifted_const_arith_operand):
(uimm_extra_bit_operand):
* config/riscv/riscv.md (*branch<ANYI:mode>_shiftedarith_equals_zero):
(*branch<ANYI:mode>_shiftedmask_equals_zero):
gcc/testsuite/ChangeLog:
* gcc.target/riscv/branch-1.c: New test.
Signed-off-by: Philipp Tomsich <philipp.tomsich@vrull.eu>
---
gcc/config/riscv/predicates.md | 23 ++++++++++
gcc/config/riscv/riscv.md | 51 +++++++++++++++++++++++
gcc/testsuite/gcc.target/riscv/branch-1.c | 37 ++++++++++++++++
3 files changed, 111 insertions(+)
create mode 100644 gcc/testsuite/gcc.target/riscv/branch-1.c
Comments
On 11/8/22 13:46, Philipp Tomsich wrote:
> gcc/ChangeLog:
>
> * config/riscv/predicates.md (shifted_const_arith_operand):
> (uimm_extra_bit_operand):
> * config/riscv/riscv.md (*branch<ANYI:mode>_shiftedarith_equals_zero):
> (*branch<ANYI:mode>_shiftedmask_equals_zero):
>
> gcc/testsuite/ChangeLog:
>
> * gcc.target/riscv/branch-1.c: New test.
Nice... It seems so obvious, but I'm not offhand aware of other ports
doing this, though many could likely benefit.
OK
jeff
Applied to master. Thanks!
Philipp.
On Fri, 18 Nov 2022 at 20:30, Jeff Law <jeffreyalaw@gmail.com> wrote:
>
> On 11/8/22 13:46, Philipp Tomsich wrote:
> > gcc/ChangeLog:
> >
> > * config/riscv/predicates.md (shifted_const_arith_operand):
> > (uimm_extra_bit_operand):
> > * config/riscv/riscv.md
> (*branch<ANYI:mode>_shiftedarith_equals_zero):
> > (*branch<ANYI:mode>_shiftedmask_equals_zero):
> >
> > gcc/testsuite/ChangeLog:
> >
> > * gcc.target/riscv/branch-1.c: New test.
>
> Nice... It seems so obvious, but I'm not offhand aware of other ports
> doing this, though many could likely benefit.
>
> OK
>
>
> jeff
>
>
>
@@ -285,3 +285,26 @@
(ior (match_operand 0 "register_operand")
(match_test "GET_CODE (op) == UNSPEC
&& (XINT (op, 1) == UNSPEC_VUNDEF)"))))
+
+;; A CONST_INT operand that consists of a single run of 32 consecutive
+;; set bits.
+(define_predicate "consecutive_bits32_operand"
+ (and (match_operand 0 "consecutive_bits_operand")
+ (match_test "popcount_hwi (UINTVAL (op)) == 32")))
+
+;; A CONST_INT operand that, if shifted down to start with its least
+;; significant non-zero bit, is a SMALL_OPERAND (suitable as an
+;; immediate to logical and arithmetic instructions).
+(define_predicate "shifted_const_arith_operand"
+ (and (match_code "const_int")
+ (match_test "ctz_hwi (INTVAL (op)) > 0")
+ (match_test "SMALL_OPERAND (INTVAL (op) >> ctz_hwi (INTVAL (op)))")))
+
+;; A CONST_INT operand that fits into the unsigned half of a
+;; signed-immediate after the top bit has been cleared.
+(define_predicate "uimm_extra_bit_operand"
+ (and (match_code "const_int")
+ (not (and (match_test "SMALL_OPERAND (INTVAL (op))")
+ (match_test "INTVAL (op) > 0")))
+ (ior (match_test "SMALL_OPERAND (UINTVAL (op) & ~(HOST_WIDE_INT_1U << floor_log2 (UINTVAL (op))))")
+ (match_test "popcount_hwi (UINTVAL (op)) == 2"))))
@@ -2205,6 +2205,57 @@
;; Conditional branches
+(define_insn_and_split "*branch<ANYI:mode>_shiftedarith_equals_zero"
+ [(set (pc)
+ (if_then_else (match_operator 1 "equality_operator"
+ [(and:ANYI (match_operand:ANYI 2 "register_operand" "r")
+ (match_operand 3 "shifted_const_arith_operand" "i"))
+ (const_int 0)])
+ (label_ref (match_operand 0 "" ""))
+ (pc)))
+ (clobber (match_scratch:ANYI 4 "=&r"))]
+ "INTVAL (operands[3]) >= 0 || !partial_subreg_p (operands[2])"
+ "#"
+ "&& reload_completed"
+ [(set (match_dup 4) (lshiftrt:ANYI (match_dup 2) (match_dup 6)))
+ (set (match_dup 4) (and:ANYI (match_dup 4) (match_dup 7)))
+ (set (pc) (if_then_else (match_op_dup 1 [(match_dup 4) (const_int 0)])
+ (label_ref (match_dup 0)) (pc)))]
+{
+ HOST_WIDE_INT mask = INTVAL (operands[3]);
+ int trailing = ctz_hwi (mask);
+
+ operands[6] = GEN_INT (trailing);
+ operands[7] = GEN_INT (mask >> trailing);
+})
+
+(define_insn_and_split "*branch<ANYI:mode>_shiftedmask_equals_zero"
+ [(set (pc)
+ (if_then_else (match_operator 1 "equality_operator"
+ [(and:ANYI (match_operand:ANYI 2 "register_operand" "r")
+ (match_operand 3 "consecutive_bits_operand" "i"))
+ (const_int 0)])
+ (label_ref (match_operand 0 "" ""))
+ (pc)))
+ (clobber (match_scratch:X 4 "=&r"))]
+ "(INTVAL (operands[3]) >= 0 || !partial_subreg_p (operands[2]))
+ && popcount_hwi (INTVAL (operands[3])) > 1
+ && !SMALL_OPERAND (INTVAL (operands[3]))"
+ "#"
+ "&& reload_completed"
+ [(set (match_dup 4) (ashift:X (subreg:X (match_dup 2) 0) (match_dup 6)))
+ (set (match_dup 4) (lshiftrt:X (match_dup 4) (match_dup 7)))
+ (set (pc) (if_then_else (match_op_dup 1 [(match_dup 4) (const_int 0)])
+ (label_ref (match_dup 0)) (pc)))]
+{
+ unsigned HOST_WIDE_INT mask = INTVAL (operands[3]);
+ int leading = clz_hwi (mask);
+ int trailing = ctz_hwi (mask);
+
+ operands[6] = GEN_INT (leading);
+ operands[7] = GEN_INT (leading + trailing);
+})
+
(define_insn "*branch<mode>_equals_zero"
[(set (pc)
(if_then_else
new file mode 100644
@@ -0,0 +1,37 @@
+/* { dg-do compile } */
+/* { dg-options "-march=rv64gc -mabi=lp64" } */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-Og" "-O1" } } */
+
+void g();
+
+void f(long long a)
+{
+ if (a & 0xff00)
+ g();
+}
+
+void f2(long long a)
+{
+ if (a & (-4ull << 3))
+ g();
+}
+
+void f3(long long a)
+{
+ if (a & 0xffff00)
+ g();
+}
+
+void f4(long long a)
+{
+ if (a & 0x7ff800)
+ g();
+}
+
+/* { dg-final { scan-assembler-times "slli\t" 2 } } */
+/* { dg-final { scan-assembler-times "srli\t" 3 } } */
+/* { dg-final { scan-assembler-times "andi\t" 1 } } */
+/* { dg-final { scan-assembler-times "\tli\t" 1 } } */
+/* { dg-final { scan-assembler-not "addi\t" } } */
+/* { dg-final { scan-assembler-not "and\t" } } */
+