diff options
author | Jeff Law <jlaw@ventanamicro.com> | 2024-05-02 14:06:22 -0600 |
---|---|---|
committer | Jeff Law <jlaw@ventanamicro.com> | 2024-05-02 14:07:29 -0600 |
commit | 9b54bea455e54fd138bf2b045bdcc133ed4e8a84 (patch) | |
tree | 82056ea9061d4514a2cc11598e9688a4ef14fcb9 /gcc/config/riscv/riscv.md | |
parent | bd010e904d3d29b65c8ddb06fa9cda0698337c57 (diff) | |
download | gcc-9b54bea455e54fd138bf2b045bdcc133ed4e8a84.zip gcc-9b54bea455e54fd138bf2b045bdcc133ed4e8a84.tar.gz gcc-9b54bea455e54fd138bf2b045bdcc133ed4e8a84.tar.bz2 |
[RFA][RISC-V] Improve constant synthesis for constants with 2 bits set
In doing some preparation work for using zbkb's pack instructions for constant
synthesis I figured it would be wise to get a sense of how well our constant
synthesis is actually working and address any clear issues.
So the first glaring inefficiency is in our handling of constants with a small
number of bits set. Let's start with just two bits set. There are 2016
distinct constants in that space (rv64). With Zbs enabled the absolute worst
we should ever do is two instructions (bseti+bseti). Yet we have 503 cases
where we're generating 3+ instructions when there's just two bits set in the
constant. A constant like 0x8000000000001000 generates 4 instructions!
This patch adds bseti (and indirectly binvi if we needed it) as a first class
citizen for constant synthesis. There's two components to this change.
First, we can't generate an IOR with a constant like (1 << 45) as an operand.
The IOR/XOR define_insn is in riscv.md. The constant argument for those
patterns must match an arith_operand which means its not really usable for
generating bseti directly in the cases we care about (at least one of the bits
will be in the 32..63 range and thus won't match arith_operand).
We have a few things we could do. One would be to extend the existing pattern
to incorporate bseti cases. But I suspect folks like the separation of the
base architecture (riscv.md) from the Zb* extensions (bitmanip.md). We could
also try to generate the RTL for bseti
directly, bypassing gen_fmt_ee (which forces undesirable constants into registers based on the predicate of the appropriate define_insn). Neither of these seemed particularly appealing to me.
So what I've done instead is to make ior/xor a define_expand and have the
expander allow a wider set of constant operands when Zbs is enabled. That
allows us to keep the bulk of Zb* support inside bitmanip.md and continue to
use gen_fmt_ee in the constant synthesis paths.
Note the code generation in this case is designed to first set as many bits as
we can with lui, then with addi since those can both set multiple bits at a
time. If there are any residual bits left to set we can emit bseti
instructions up to the current cost ceiling.
This results in fixing all of the 503 2-bit set cases where we emitted too many
instructions. It also significantly helps other scenarios with more bits set.
The testcase I'm including verifies the number of instructions we generate for
the full set of 2016 possible cases. Obviously this won't be possible as we
increase the number of bits (there are something like 48k cases with just 3
bits set).
gcc/
* config/riscv/predicates.md (arith_or_zbs_operand): New predicate.
* config/riscv/riscv.cc (riscv_build_integer_one): Use bseti to set
single bits when profitable.
* config/riscv/riscv.md (*<optab><mode>3): Renamed with '*' prefix.
(<optab><mode>3): New expander for IOR/XOR.
gcc/testsuite
* gcc.target/riscv/synthesis-1.c: New test.
Diffstat (limited to 'gcc/config/riscv/riscv.md')
-rw-r--r-- | gcc/config/riscv/riscv.md | 17 |
1 files changed, 16 insertions, 1 deletions
diff --git a/gcc/config/riscv/riscv.md b/gcc/config/riscv/riscv.md index 8f518fd..b9b0acf 100644 --- a/gcc/config/riscv/riscv.md +++ b/gcc/config/riscv/riscv.md @@ -1639,7 +1639,22 @@ [(set_attr "type" "logical") (set_attr "mode" "<MODE>")]) -(define_insn "<optab><mode>3" +;; When we construct constants we may want to twiddle a single bit +;; by generating an IOR. But the constant likely doesn't fit +;; arith_operand. So the generic code will reload the constant into +;; a register. Post-reload we won't have the chance to squash things +;; back into a Zbs insn. +;; +;; So indirect through a define_expand. That allows us to have a +;; predicate that conditionally accepts single bit constants without +;; putting the details of Zbs instructions in here. +(define_expand "<optab><mode>3" + [(set (match_operand:X 0 "register_operand") + (any_or:X (match_operand:X 1 "register_operand" "") + (match_operand:X 2 "arith_or_zbs_operand" "")))] + "") + +(define_insn "*<optab><mode>3" [(set (match_operand:X 0 "register_operand" "=r,r") (any_or:X (match_operand:X 1 "register_operand" "%r,r") (match_operand:X 2 "arith_operand" " r,I")))] |