]> git.ipfire.org Git - thirdparty/gcc.git/commit
[V2][RISC-V] Synthesize more efficient IOR/XOR sequences
authorShreya Munnangi <smunnangi1@ventanamicro.com>
Sat, 10 May 2025 13:18:33 +0000 (07:18 -0600)
committerJeff Law <jlaw@ventanamicro.com>
Sat, 10 May 2025 13:25:24 +0000 (07:25 -0600)
commit36daa0da95fba18d0d4eb4e10fa07ac3b76fa426
treef995c17e3def3960396c21dc2e23f8f6000495fa
parentee7c0a5b70dc316477f45abc0f09dd2af9abe5cb
[V2][RISC-V] Synthesize more efficient IOR/XOR sequences

So mvconst_internal's primary benefit is in constant synthesis not impacting
the combine budget in terms of the number of instructions it is willing to
combine together at any given time.  The downside is mvconst_internal breaks
combine's toplevel costing model and as a result many other patterns have to be
implemented as define_insn_and_splits rather than the often more natural
define_splits.

This primarily impacts logical operations where we want to see the constant
operand and potentially simplify the logical with other nearby logicals or
shifts.

We can reduce our reliance on mvconst_internal and generate better code for
various cases by generating better initial code for logical operations.

So let's assume we have a inclusive-or of a register with a nontrivial
constant.  Right now we will load the nontrivial constant into a new pseudo
(using multiple instructions), then emit a two register source ior operation.

For some cases we can just generate the code we want at expansion time.
Concretely let's take this testcase:

> unsigned long foo(unsigned long src) { return src | 0x8800000000000007; }

Right now we generate this code:

>         li      a5,-15
>         slli    a5,a5,59
>         addi    a5,a5,7
>         or      a0,a0,a5

The first three instructions are synthesizing the constant.  The last
instruction performs the desired operation.  But we can do better:

>         ori     a0,a0,7
>         bseti   a0,a0,59
>         bseti   a0,a0,63

Notice how we never even bother to synthesize the constant.

IOR/XOR are pretty simple and this patch focuses exclusively on those. We use
[x]ori to set whatever low 11 bits we need, then bset/binv for a small number
of higher bits.  We use the cost of constant synthesis as our budget.

We also support a couple special cases.  First, we might be able to rotate the
source value such that all the bits we want to manipulate are in the low 11
bits.  So we rotate the source, manipulate the bits, then rotate things back to
where they belong.  I didn't see this trigger in spec, but I did trivially find
a testcase where it was likely faster.

Second, we can have cases where we want to invert most of the bits, but a small
number are supposed to be preserved.  We can pre-flip the bits we want to
preserve with binv, then invert the whole register with not (which puts the
bits to be preserved back in their original state).

I suspect there are likely a few more cases that could be improved, but the
patch should stand on its own now and getting it out of the way allows us to
focus on logical AND which is far tougher, but also more important in the task
of removing mvconst_internal.

As we're not removing mvconst_internal yet, this patch is mostly a nop. I did
look at spec before/after and didn't see anything particular interesting.  I
also temporarily removed mvconst_internal and looked at spec before/after to
hopefully ensure we weren't missing anything obvious in the XOR/IOR cases.
Obviously that latter test showed all kinds of regressions with AND.

We're still working through implementation details on the AND case and
determining what bridge patterns we're going to need to ensure we don't
regress.   But this XOR/IOR patch is in good enough shape that it can go
forward now.

Naturally this has been run through my tester (bootstrap & regression test is
in flight, but won't finish for many more hours).  Obviously I'm quite
interested in anything spit out by the pre-commit CI system.

gcc/

* config/riscv/iterators.md (OPTAB): New iterator.
* config/riscv/predicates.md (arith_or_zbs_operand): Remove.
(reg_or_const_int_operand): New predicate.
* config/riscv/riscv-protos.h (synthesize_ior_xor): Prototype.
* config/riscv/riscv.cc (synthesize_ior_xor): New function.
* config/riscv/riscv.md (ior/xor expander): Use synthesize_ior_xor.

gcc/testsuite/

* gcc.target/riscv/ior-synthesis-1.c: New test.
* gcc.target/riscv/ior-synthesis-2.c: New test.
* gcc.target/riscv/xor-synthesis-1.c: New test.
* gcc.target/riscv/xor-synthesis-2.c: New test.
* gcc.target/riscv/xor-synthesis-3.c: New test.

Co-authored-by: Jeff Law <jlaw@ventanamicro.com>
gcc/config/riscv/iterators.md
gcc/config/riscv/predicates.md
gcc/config/riscv/riscv-protos.h
gcc/config/riscv/riscv.cc
gcc/config/riscv/riscv.md
gcc/testsuite/gcc.target/riscv/ior-synthesis-1.c [new file with mode: 0644]
gcc/testsuite/gcc.target/riscv/ior-synthesis-2.c [new file with mode: 0644]
gcc/testsuite/gcc.target/riscv/xor-synthesis-1.c [new file with mode: 0644]
gcc/testsuite/gcc.target/riscv/xor-synthesis-2.c [new file with mode: 0644]
gcc/testsuite/gcc.target/riscv/xor-synthesis-3.c [new file with mode: 0644]