https://gcc.gnu.org/g:8363c2392832a6600e49d4ac8361bf3e2052601b

commit r17-2120-g8363c2392832a6600e49d4ac8361bf3e2052601b
Author: Jeff Law <[email protected]>
Date:   Fri Jul 3 07:50:13 2026 -0600

    [RISC-V] Utilize shNadd.uw during ADD synthesis
    
    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.
    
    gcc/
            * config/riscv/riscv.cc (synthesize_add): Utilize shNadd.uw when
            appropriate.

Diff:
---
 gcc/config/riscv/riscv.cc | 40 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 40 insertions(+)

diff --git a/gcc/config/riscv/riscv.cc b/gcc/config/riscv/riscv.cc
index d5d073f37a46..251da7c09998 100644
--- a/gcc/config/riscv/riscv.cc
+++ b/gcc/config/riscv/riscv.cc
@@ -16251,6 +16251,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]);

Reply via email to