So this is the next area to improve in the ALU synthesis code. In this
case we're looking to improve ADD synthesis. In particular we're
missing the ability to utilize the shNadd.uw instructions. In RTL these
look like:
(define_insn "*shNadduw"
[(set (match_operand:DI 0 "register_operand" "=r")
(plus:DI
(and:DI (ashift:DI (match_operand:DI 1 "register_operand" "r")
(match_operand:QI 2 "imm123_operand" "Ds3"))
(match_operand 3 "immediate_operand" "n"))
(match_operand:DI 4 "register_operand" "r")))]
"TARGET_64BIT && TARGET_ZBA
&& (INTVAL (operands[3]) >> INTVAL (operands[2])) == 0xffffffff"
In particular note the masking. Essentially we're zero extending the
shifted operand from SI to DI before shifting. That's the only
difference from the more common shNadd insns. Since we already support
using shNadd for ADD synthesis the number of opportunities to exploit
the .uw variant is relatively limited. Given x + C the relevant values
of C have bit 31+N set (where N is 1, 2 or 3) and bits 32+N clear.
Additionally the low 12+N bits must be clear. Otherwise there are other
ways to synthesize the addition. But for those limited constants we can
use lui+shNadd.uw sequence. Concretely consider:
unsigned long foo(unsigned long src) { return src + 0x1ffffe000; }
That currently generates:
li a5,-4096
slli.uw a5,a5,1
add a0,a0,a5
Instead we want to generate:
li a5,-4096
sh1add.uw a0,a5,a0
You could legitimately ask why this isn't a simple combine pattern to
squash the slliw and add together. Enter our friend mvconst_internal.
Its existence encourages GCC to use the constant 0x1ffffe000 as-is in
the RTL. So we end up with the original x+C case again and
mvconst_internal expands the constant back into li+slli.uw after combine.
And that's also the reason why this has no testcase. We get the code we
want during expansion, combine+mvconst_internal do their thing and undo
our carefully crafted RTL, at least in the small isolated tests I've
looked at. So at this time the patch is largely a NOP, but it's a step
on the path to mvconst_internal as it's another set of complex constants
we can avoid synthesizing at least some of the time. It's also possible
the early code generation survives in larger contexts, I haven't really
looked at that.
Given the difficulty in testing, I've mostly relied on looking at the
expansion code manually. Not great. But just in case this has been
tested on riscv32-elf & riscv64-elf without regressions. It's also
bootstrapped and regression tested on the c920 and k3. Waiting on
pre-commit CI before moving forward.
Jeff
diff --git a/gcc/config/riscv/riscv.cc b/gcc/config/riscv/riscv.cc
index d5eab5421318..c2211aeb0276 100644
--- a/gcc/config/riscv/riscv.cc
+++ b/gcc/config/riscv/riscv.cc
@@ -16195,6 +16195,46 @@ synthesize_add (rtx operands[3])
return true;
}
+ /* We can use shNadd.uw for some cases. These are at most 2 insns. */
+ ival = INTVAL (operands[2]);
+ if (TARGET_64BIT && TARGET_ZBA && budget1 >= 2)
+ {
+ int shcount = 0;
+ rtx mask;
+
+ if ((ival & 0x1fff) == 0
+ && (ival & HOST_WIDE_INT_C (0x100000000)) != 0
+ && (ival & HOST_WIDE_INT_C (0xfffffffe00000000)) == 0)
+ {
+ mask = GEN_INT (HOST_WIDE_INT_C (0x1fffffffe));
+ shcount = 1;
+ }
+ else if ((ival & 0x3fff) == 0
+ && (ival & HOST_WIDE_INT_C (0x200000000)) != 0
+ && (ival & HOST_WIDE_INT_C (0xfffffffc00000000)) == 0)
+ {
+ mask = GEN_INT (HOST_WIDE_INT_C (0x3fffffffc));
+ shcount = 2;
+ }
+ else if ((ival & 0x7fff) == 0
+ && (ival & HOST_WIDE_INT_C (0x400000000)) != 0
+ && (ival & HOST_WIDE_INT_C (0xfffffff800000000)) == 0)
+ {
+ mask = GEN_INT (HOST_WIDE_INT_C (0x7fffffff8));
+ shcount = 3;
+ }
+
+ if (shcount != 0)
+ {
+ rtx x = force_reg (word_mode, GEN_INT (sext_hwi (ival >> shcount,
32)));
+ x = gen_rtx_ASHIFT (word_mode, x, GEN_INT (shcount));
+ x = gen_rtx_AND (word_mode, x, mask);
+ x = gen_rtx_PLUS (word_mode, x, operands[1]);
+ emit_insn (gen_rtx_SET (operands[0], x));
+ return true;
+ }
+ }
+
/* If we can shift the constant by 1, 2, or 3 bit positions
and the result is a cheaper constant, then do so. */
ival = INTVAL (operands[2]);