]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
[RISC-V] Infrastructure of synthesizing logical AND with constant
authorShreya Munnangi <smunnangi1@ventanamicro.com>
Wed, 21 May 2025 02:15:42 +0000 (20:15 -0600)
committerJeff Law <jlaw@ventanamicro.com>
Wed, 21 May 2025 02:17:17 +0000 (20:17 -0600)
So this is the next step on the path to mvconst_internal removal and is work
from Shreya and myself.

This puts in the infrastructure to allow us to synthesize logical AND much like
we're doing with logical IOR/XOR.

Unlike IOR/XOR, AND has many more special cases that can be profitable. For
example, you can use shifts to clear many bits.  You can use zero extension to
clear bits, you can use rotate+andi+rotate, shift pairs, etc.

So to make potential bisecting easy the plan is to drop in the work on logical
AND in several steps, essentially one new case at a time.

This step just puts the basics of a operation synthesis in place.  It still
uses the same code generation strategies as we are currently using.

I'd like to say this is NFC, but unfortunately that's not true.  While the code
generation strategy is the same, this does indirectly introduce new REG_EQUAL
notes.  Those additional notes in turn can impact how various optimizers behave
in very minor ways.

As usual, this has survived my tester on riscv32-elf and riscv64-elf.

Waiting on pre-commit to do its thing.  And I'll start queuing up the
additional cases we want to handle while waiting ðŸ˜‰

gcc/
* config/riscv/riscv-protos.h (synthesize_and): Prototype.
* config/riscv/riscv.cc (synthesize_and): New function.
* config/riscv/riscv.md (and<mode>3): Use it.

Co-Authored-By: Jeff Law <jlaw@ventanamicro.com>
gcc/config/riscv/riscv-protos.h
gcc/config/riscv/riscv.cc
gcc/config/riscv/riscv.md

index b39b858acac81a5377536ce3f76a555c55d7a894..d8c8f6b5079f2275f7eb6145004b69782a6d49fa 100644 (file)
@@ -141,6 +141,7 @@ extern void riscv_expand_ustrunc (rtx, rtx);
 extern void riscv_expand_sstrunc (rtx, rtx);
 extern int riscv_register_move_cost (machine_mode, reg_class_t, reg_class_t);
 extern bool synthesize_ior_xor (rtx_code, rtx [3]);
+extern bool synthesize_and (rtx [3]);
 
 #ifdef RTX_CODE
 extern void riscv_expand_int_scc (rtx, enum rtx_code, rtx, rtx, bool *invert_ptr = 0);
index 4c5bb02754df64dc0c225202b07adcf8b75fc6ea..1a88e96d8c6f8a040cbba2899d08b0d5ec88284e 100644 (file)
@@ -14300,7 +14300,7 @@ synthesize_ior_xor (rtx_code code, rtx operands[3])
      we clear bits in IVAL.  Once IVAL is zero, then synthesis of the
      operation is complete.  */
   unsigned HOST_WIDE_INT ival = INTVAL (operands[2]);
-  
+
   /* Check if we want to use [x]ori. Then get the remaining bits
      and decrease the budget by one. */
   if ((ival & HOST_WIDE_INT_UC (0x7ff)) != 0)
@@ -14435,14 +14435,14 @@ synthesize_ior_xor (rtx_code code, rtx operands[3])
        }
     }
 
-  /* If after accounting for bseti the remaining budget has 
+  /* If after accounting for bseti the remaining budget has
      gone to less than zero, it forces the value into a
      register and performs the IOR operation.  It returns
      TRUE to the caller so the caller knows code generation
      is complete. */
   if (budget < 0)
     {
-      rtx x = force_reg (word_mode, operands[2]); 
+      rtx x = force_reg (word_mode, operands[2]);
       x = gen_rtx_fmt_ee (code, word_mode, operands[1], x);
       emit_insn (gen_rtx_SET (operands[0], x));
       return true;
@@ -14466,8 +14466,8 @@ synthesize_ior_xor (rtx_code code, rtx operands[3])
     }
 
   /* We figure out a single bit as a constant and
-     generate a CONST_INT node for that.  Then we 
-     construct the IOR node, then the SET node and 
+     generate a CONST_INT node for that.  Then we
+     construct the IOR node, then the SET node and
      emit it.  An IOR with a suitable constant that is
      a single bit will be implemented with a bseti. */
   while (ival)
@@ -14486,6 +14486,60 @@ synthesize_ior_xor (rtx_code code, rtx operands[3])
   return true;
 }
 
+/* Synthesize OPERANDS[0] = OPERANDS[1] & OPERANDS[2].
+
+    OPERANDS[0] and OPERANDS[1] will be a REG and may be the same
+    REG.
+
+    OPERANDS[2] is a CONST_INT.
+
+    Return TRUE if the operation was fully synthesized and the caller
+    need not generate additional code.  Return FALSE if the operation
+    was not synthesized and the caller is responsible for emitting the
+    proper sequence.  */
+
+bool
+synthesize_and (rtx operands[3])
+{
+  /* Trivial cases that don't need synthesis.  */
+  if (SMALL_OPERAND (INTVAL (operands[2]))
+     || (TARGET_ZBS && not_single_bit_mask_operand (operands[2], word_mode)))
+    return false;
+
+  /* If the second operand is a mode mask, emit an extension
+     insn instead.  */
+  if (CONST_INT_P (operands[2]))
+    {
+      enum machine_mode tmode = VOIDmode;
+      if (UINTVAL (operands[2]) == GET_MODE_MASK (HImode))
+       tmode = HImode;
+      else if (UINTVAL (operands[2]) == GET_MODE_MASK (SImode))
+       tmode = SImode;
+
+      if (tmode != VOIDmode)
+       {
+         rtx tmp = gen_lowpart (tmode, operands[1]);
+         emit_insn (gen_extend_insn (operands[0], tmp, word_mode, tmode, 1));
+         return true;
+       }
+    }
+
+  /* If the remaining budget has gone to less than zero, it
+     forces the value into a register and performs the AND
+     operation.  It returns TRUE to the caller so the caller
+     knows code generation is complete.
+     FIXME: This is hacked to always be enabled until the last
+     patch in the series is enabled.  */
+  if (1)
+    {
+      rtx x = force_reg (word_mode, operands[2]);
+      x = gen_rtx_AND (word_mode, operands[1], x);
+      emit_insn (gen_rtx_SET (operands[0], x));
+      return true;
+    }
+}
+
+
 /* Initialize the GCC target structure.  */
 #undef TARGET_ASM_ALIGNED_HI_OP
 #define TARGET_ASM_ALIGNED_HI_OP "\t.half\t"
index 1a56cc8580986b9d3fa2fdae55a7064973ab3f1b..7f6d0bbab3eb77599772fea176c24193c56936f2 100644 (file)
 (define_expand "and<mode>3"
   [(set (match_operand:X                0 "register_operand")
         (and:X (match_operand:X 1 "register_operand")
-              (match_operand:X 2 "arith_or_mode_mask_or_zbs_operand")))]
+              (match_operand:X 2 "reg_or_const_int_operand")))]
   ""
 {
-  /* If the second operand is a mode mask, emit an extension
-     insn instead.  */
-  if (CONST_INT_P (operands[2]))
-    {
-      enum machine_mode tmode = VOIDmode;
-      if (UINTVAL (operands[2]) == GET_MODE_MASK (HImode))
-       tmode = HImode;
-      else if (UINTVAL (operands[2]) == GET_MODE_MASK (SImode))
-       tmode = SImode;
-
-      if (tmode != VOIDmode)
-       {
-         rtx tmp = gen_lowpart (tmode, operands[1]);
-         emit_insn (gen_extend_insn (operands[0], tmp, <MODE>mode, tmode, 1));
-         DONE;
-       }
-    }
+  if (CONST_INT_P (operands[2]) && synthesize_and (operands))
+    DONE;
 })
 
 (define_insn "*and<mode>3"