[08/21] Add poly manipulation

Message ID 20220531085012.269719-9-juzhe.zhong@rivai.ai
State Committed
Headers
Series *** Add RVV (RISC-V 'V' Extension) support *** |

Commit Message

juzhe.zhong@rivai.ai May 31, 2022, 8:49 a.m. UTC
  From: zhongjuzhe <juzhe.zhong@rivai.ai>

gcc/ChangeLog:

        * config/riscv/riscv-protos.h (riscv_add_offset): Change riscv_add_offset as global function.
        * config/riscv/riscv-vector.cc (rvv_report_required): New function.
        (expand_quotient): New function.
        (rvv_expand_poly_move): New function.
        * config/riscv/riscv-vector.h (rvv_report_required): New function.
        (rvv_expand_poly_move): New function.
        * config/riscv/riscv.cc (riscv_const_insns): Fix no return value bug.
        (riscv_split_symbol): Add symbol_ref with poly_int support.
        (riscv_legitimize_const_move): Add const poly_int move support.
        (riscv_legitimize_move): Add const poly_int move support.
        (riscv_hard_regno_mode_ok): Add VL_REGNUM and VTYPE_REGNUM register allocation.
        (riscv_conditional_register_usage): Fix RVV registers.
        
---
 gcc/config/riscv/riscv-protos.h  |   1 +
 gcc/config/riscv/riscv-vector.cc | 254 +++++++++++++++++++++++++++++++
 gcc/config/riscv/riscv-vector.h  |   2 +
 gcc/config/riscv/riscv.cc        |  46 +++++-
 4 files changed, 299 insertions(+), 4 deletions(-)
  

Patch

diff --git a/gcc/config/riscv/riscv-protos.h b/gcc/config/riscv/riscv-protos.h
index 618eb746eaa..2d63fe76930 100644
--- a/gcc/config/riscv/riscv-protos.h
+++ b/gcc/config/riscv/riscv-protos.h
@@ -74,6 +74,7 @@  extern bool riscv_expand_block_move (rtx, rtx, rtx);
 extern bool riscv_store_data_bypass_p (rtx_insn *, rtx_insn *);
 extern rtx riscv_gen_gpr_save_insn (struct riscv_frame_info *);
 extern bool riscv_gpr_save_operation_p (rtx);
+extern rtx riscv_add_offset (rtx, rtx, HOST_WIDE_INT);
 
 /* Routines implemented in riscv-c.cc.  */
 void riscv_cpu_cpp_builtins (cpp_reader *);
diff --git a/gcc/config/riscv/riscv-vector.cc b/gcc/config/riscv/riscv-vector.cc
index 4b2fe2a8d11..d09fc1b8e49 100644
--- a/gcc/config/riscv/riscv-vector.cc
+++ b/gcc/config/riscv/riscv-vector.cc
@@ -592,6 +592,260 @@  rvv_const_vec_all_same_in_range_p (rtx x, HOST_WIDE_INT minval,
           IN_RANGE (INTVAL (elt), minval, maxval));
 }
 
+/* Report when we try to do something that requires vector when vector is disabled.
+   This is an error of last resort and isn't very high-quality.  It usually
+   involves attempts to measure the vector length in some way.  */
+void
+rvv_report_required (void)
+{
+  static bool reported_p = false;
+
+  /* Avoid reporting a slew of messages for a single oversight.  */
+  if (reported_p)
+    return;
+
+  error ("this operation requires the RVV ISA extension");
+  inform (input_location, "you can enable RVV using the command-line"
+          " option %<-march%>, or by using the %<target%>"
+          " attribute or pragma");
+  reported_p = true;
+}
+
+/* Note: clobber register holds the vlenb or 1/2 vlenb or 1/4 vlenb or 1/8 vlenb value. */
+/* Expand move for quotient.  */
+static void
+expand_quotient (int quotient, machine_mode mode, rtx clobber_vlenb, rtx dest)
+{
+  if (quotient == 0)
+    {
+      riscv_emit_move(dest, GEN_INT(0));
+      return;
+    }
+
+  bool is_neg = quotient < 0;
+  quotient = abs(quotient);
+  int log2 = exact_log2 (quotient);
+  int vlenb = BYTES_PER_RISCV_VECTOR.coeffs[1];
+
+  if (GET_MODE_SIZE (mode).to_constant () <= GET_MODE_SIZE (Pmode))
+    emit_insn (gen_rtx_SET (clobber_vlenb, gen_int_mode (poly_int64 (vlenb, vlenb), mode)));
+  else
+    {
+      riscv_emit_move (gen_highpart (Pmode, clobber_vlenb), GEN_INT (0));
+      emit_insn (gen_rtx_SET (gen_lowpart (Pmode, clobber_vlenb), gen_int_mode (poly_int64 (vlenb, vlenb), Pmode)));
+    }
+
+  if (log2 == 0)
+    {
+      if (is_neg)
+        {
+          if (GET_MODE_SIZE (mode).to_constant () <= GET_MODE_SIZE (Pmode))
+            emit_insn (gen_rtx_SET (dest, gen_rtx_NEG (mode, clobber_vlenb)));
+          else
+            {
+              /* We should use SImode to simulate DImode negation. */
+              /* prologue and epilogue can not go through this condition. */
+              gcc_assert (can_create_pseudo_p ());
+              rtx reg = gen_reg_rtx (Pmode);
+              riscv_emit_move(dest, clobber_vlenb);
+              emit_insn (gen_rtx_SET (reg,
+                  gen_rtx_NE (Pmode, gen_lowpart (Pmode, dest), const0_rtx)));
+              emit_insn (gen_rtx_SET (gen_highpart (Pmode, dest),
+                  gen_rtx_NEG (Pmode, gen_highpart (Pmode, dest))));
+              emit_insn (gen_rtx_SET (gen_lowpart (Pmode, dest),
+                  gen_rtx_NEG (Pmode, gen_lowpart (Pmode, dest))));
+              emit_insn (gen_rtx_SET (gen_highpart (Pmode, dest),
+                  gen_rtx_MINUS (Pmode, gen_highpart (Pmode, dest), reg)));
+            }
+        }
+      else
+        riscv_emit_move(dest, clobber_vlenb);
+    }
+  else if (log2 != -1
+    && GET_MODE_SIZE (mode).to_constant () <= GET_MODE_SIZE (Pmode))
+    {
+      gcc_assert (IN_RANGE (log2, 0, 31));
+
+      if (is_neg)
+        {
+          emit_insn (gen_rtx_SET (dest, gen_rtx_NEG (mode, clobber_vlenb)));
+          emit_insn (gen_rtx_SET (dest, gen_rtx_ASHIFT (mode, dest, GEN_INT (log2))));
+        }
+      else
+        emit_insn (gen_rtx_SET (dest, gen_rtx_ASHIFT (mode, clobber_vlenb, GEN_INT (log2))));
+    }
+  else if (exact_log2 (quotient + 1) != -1
+    && GET_MODE_SIZE (mode).to_constant () <= GET_MODE_SIZE (Pmode))
+    {
+      gcc_assert (IN_RANGE (exact_log2 (quotient + 1), 0, 31));
+      emit_insn (gen_rtx_SET (
+                   dest,
+                   gen_rtx_ASHIFT (mode, clobber_vlenb, GEN_INT (exact_log2 (quotient + 1)))));
+
+      if (is_neg)
+        emit_insn (gen_rtx_SET (dest, gen_rtx_MINUS (mode, clobber_vlenb, dest)));
+      else
+        emit_insn (gen_rtx_SET (dest, gen_rtx_MINUS (mode, dest, clobber_vlenb)));
+    }
+  else if (exact_log2 (quotient - 1) != -1
+    && GET_MODE_SIZE (mode).to_constant () <= GET_MODE_SIZE (Pmode))
+    {
+      gcc_assert (IN_RANGE (exact_log2 (quotient - 1), 0, 31));
+      emit_insn (gen_rtx_SET (
+                   dest, gen_rtx_ASHIFT (mode, clobber_vlenb,
+                                         GEN_INT (exact_log2 (quotient - 1)))));
+
+      if (is_neg)
+        {
+          emit_insn (gen_rtx_SET (dest, gen_rtx_NEG (mode, dest)));
+          emit_insn (gen_rtx_SET (dest, gen_rtx_MINUS (mode, dest, clobber_vlenb)));
+        }
+      else
+        emit_insn (gen_rtx_SET (dest, gen_rtx_PLUS (mode, dest, clobber_vlenb)));
+    }
+  else
+    {
+      gcc_assert (TARGET_MUL
+                  && "M-extension must be enabled to calculate the poly_int "
+                  "size/offset.");
+
+      if (is_neg)
+        riscv_emit_move (dest, GEN_INT (-quotient));
+      else
+        riscv_emit_move (dest, GEN_INT (quotient));
+
+      if (GET_MODE_SIZE (mode).to_constant () <= GET_MODE_SIZE (Pmode))
+        emit_insn (gen_rtx_SET (dest, gen_rtx_MULT (mode, dest, clobber_vlenb)));
+      else
+        {
+          /* We should use SImode to simulate DImode multiplication. */
+          /* prologue and epilogue can not go through this condition. */
+          gcc_assert (can_create_pseudo_p ());
+          rtx reg = gen_reg_rtx (Pmode);
+          emit_insn (gen_umulsi3_highpart (reg, gen_lowpart (Pmode, dest),
+              gen_lowpart (Pmode, clobber_vlenb)));
+          emit_insn (gen_rtx_SET (gen_highpart (Pmode, clobber_vlenb),
+              gen_rtx_MULT (Pmode, gen_highpart (Pmode, clobber_vlenb),
+                  gen_lowpart (Pmode, dest))));
+          emit_insn (gen_rtx_SET (gen_highpart (Pmode, dest),
+              gen_rtx_MULT (Pmode, gen_highpart (Pmode, dest),
+                  gen_lowpart (Pmode, clobber_vlenb))));
+          emit_insn (gen_rtx_SET (gen_lowpart (Pmode, dest),
+              gen_rtx_MULT (Pmode, gen_lowpart (Pmode, dest),
+                  gen_lowpart (Pmode, clobber_vlenb))));
+          emit_insn (gen_rtx_SET (gen_highpart (Pmode, dest),
+              gen_rtx_PLUS (Pmode, gen_highpart (Pmode, dest),
+                  gen_highpart (Pmode, clobber_vlenb))));
+          emit_insn (gen_rtx_SET (gen_highpart (Pmode, dest),
+              gen_rtx_PLUS (Pmode, gen_highpart (Pmode, dest), reg)));
+        }
+    }
+}
+
+/* Analyze src and emit const_poly_int mov sequence.  */
+
+void
+rvv_expand_poly_move (machine_mode mode, rtx dest, rtx clobber, rtx src)
+{
+  poly_int64 value = rtx_to_poly_int64 (src);
+  int offset = value.coeffs[0];
+  int factor = value.coeffs[1];
+  int vlenb = BYTES_PER_RISCV_VECTOR.coeffs[1];
+  int div_factor = 0;
+
+  if (BYTES_PER_RISCV_VECTOR.is_constant ())
+    {
+      gcc_assert (value.is_constant ());
+      riscv_emit_move (dest, GEN_INT (value.to_constant ()));
+      return;
+    }
+  else if ((factor % vlenb) == 0)
+    expand_quotient (factor / vlenb, mode, clobber, dest);
+  else if ((factor % (vlenb / 2)) == 0)
+    {
+      expand_quotient (factor / (vlenb / 2), mode, clobber, dest);
+      div_factor = 2;
+    }
+  else if ((factor % (vlenb / 4)) == 0)
+    {
+      expand_quotient (factor / (vlenb / 4), mode, clobber, dest);
+      div_factor = 4;
+    }
+  else if ((factor % (vlenb / 8)) == 0)
+    {
+      expand_quotient (factor / (vlenb / 8), mode, clobber, dest);
+      div_factor = 8;
+    }
+  else if ((factor % (vlenb / 16)) == 0)
+    {
+      expand_quotient (factor / (vlenb / 16), mode, clobber, dest);
+      div_factor = 16;
+    }
+  else
+    gcc_unreachable ();
+
+  if (div_factor != 0)
+    {
+      if (GET_MODE_SIZE (mode).to_constant () <= GET_MODE_SIZE (Pmode))
+        emit_insn (gen_rtx_SET (
+            dest,
+            gen_rtx_ASHIFTRT (mode, dest, GEN_INT (exact_log2 (div_factor)))));
+      else
+        {
+          /* We should use SImode to simulate DImode shift. */
+          /* prologue and epilogue can not go through this condition. */
+          gcc_assert (can_create_pseudo_p ());
+          rtx reg = gen_reg_rtx (Pmode);
+          emit_insn (gen_rtx_SET (
+              reg, gen_rtx_ASHIFT (Pmode, gen_highpart (Pmode, dest),
+                                   GEN_INT (GET_MODE_BITSIZE (Pmode) -
+                                            exact_log2 (div_factor)))));
+          emit_insn (gen_rtx_SET (
+              gen_lowpart (Pmode, dest),
+              gen_rtx_LSHIFTRT (Pmode, gen_lowpart (Pmode, dest),
+                                GEN_INT (exact_log2 (div_factor)))));
+          emit_insn (gen_rtx_SET (
+              gen_lowpart (Pmode, dest),
+              gen_rtx_IOR (Pmode, reg, gen_lowpart (Pmode, dest))));
+          emit_insn (gen_rtx_SET (
+              gen_highpart (Pmode, dest),
+              gen_rtx_ASHIFTRT (Pmode, gen_highpart (Pmode, dest),
+                                GEN_INT (exact_log2 (div_factor)))));
+        }
+    }
+
+  HOST_WIDE_INT constant = offset - factor;
+
+  if (constant == 0)
+    return;
+  else if (SMALL_OPERAND (constant))
+    {
+      if (GET_MODE_SIZE (mode).to_constant () <= GET_MODE_SIZE (Pmode))
+        emit_insn (
+            gen_rtx_SET (dest, gen_rtx_PLUS (mode, dest, GEN_INT (constant))));
+      else
+        {
+          /* We should use SImode to simulate DImode addition. */
+          /* prologue and epilogue can not go through this condition. */
+          gcc_assert (can_create_pseudo_p ());
+          rtx reg = gen_reg_rtx (Pmode);
+          emit_insn (
+              gen_rtx_SET (reg, gen_rtx_PLUS (Pmode, gen_lowpart (Pmode, dest),
+                                              GEN_INT (constant))));
+          emit_insn (gen_rtx_SET (
+              gen_lowpart (Pmode, dest),
+              gen_rtx_LTU (Pmode, reg, gen_lowpart (Pmode, dest))));
+          emit_insn (
+              gen_rtx_SET (gen_highpart (Pmode, dest),
+                           gen_rtx_PLUS (Pmode, gen_lowpart (Pmode, dest),
+                                         gen_highpart (Pmode, dest))));
+          riscv_emit_move (gen_lowpart (Pmode, dest), reg);
+        }
+    }
+  else
+    emit_insn (gen_rtx_SET (dest, riscv_add_offset (clobber, dest, constant)));
+}
+
 /* Helper functions for handling sew=64 on RV32 system. */
 bool
 imm32_p (rtx a)
diff --git a/gcc/config/riscv/riscv-vector.h b/gcc/config/riscv/riscv-vector.h
index e93852e3e56..b70cf676e26 100644
--- a/gcc/config/riscv/riscv-vector.h
+++ b/gcc/config/riscv/riscv-vector.h
@@ -20,4 +20,6 @@ 
 
 #ifndef GCC_RISCV_VECTOR_H
 #define GCC_RISCV_VECTOR_H
+void rvv_report_required (void);
+void rvv_expand_poly_move (machine_mode, rtx, rtx, rtx);
 #endif // GCC_RISCV_VECTOR_H
\ No newline at end of file
diff --git a/gcc/config/riscv/riscv.cc b/gcc/config/riscv/riscv.cc
index fc27dc957dc..7a1f19b32ee 100644
--- a/gcc/config/riscv/riscv.cc
+++ b/gcc/config/riscv/riscv.cc
@@ -1164,6 +1164,7 @@  riscv_const_insns (rtx x)
 	          factor = 1 + riscv_integer_cost (INTVAL (elt));
 	      }
 	  }
+	return factor;
       }
 
     case CONST:
@@ -1467,7 +1468,7 @@  riscv_split_symbol (rtx temp, rtx addr, machine_mode mode, rtx *low_out,
    riscv_force_temporary; it is only needed when OFFSET is not a
    SMALL_OPERAND.  */
 
-static rtx
+rtx
 riscv_add_offset (rtx temp, rtx reg, HOST_WIDE_INT offset)
 {
   if (!SMALL_OPERAND (offset))
@@ -1723,7 +1724,20 @@  riscv_legitimize_const_move (machine_mode mode, rtx dest, rtx src)
       riscv_emit_move (dest, riscv_add_offset (NULL, base, INTVAL (offset)));
       return;
     }
-
+    
+  /* Handle (const:SI (plus:SI (symbol_ref:SI)
+        (const_poly_int:SI [16, 16]))) */
+  if (GET_CODE (src) == CONST && GET_CODE (XEXP (src, 0)) == PLUS 
+      && CONST_POLY_INT_P (XEXP (XEXP (src, 0), 1)))
+    {
+      rtx reg = gen_reg_rtx (mode);
+      rtx clobber = gen_reg_rtx (mode);
+      riscv_emit_move (dest, XEXP (XEXP (src, 0), 0));
+      rvv_expand_poly_move (mode, reg, clobber, XEXP (XEXP (src, 0), 1));
+      emit_insn (gen_rtx_SET (dest, gen_rtx_PLUS (mode, dest, reg)));
+      return;
+    }
+    
   src = force_const_mem (mode, src);
 
   /* When using explicit relocs, constant pool references are sometimes
@@ -1738,6 +1752,28 @@  riscv_legitimize_const_move (machine_mode mode, rtx dest, rtx src)
 bool
 riscv_legitimize_move (machine_mode mode, rtx dest, rtx src)
 {
+  scalar_int_mode int_mode;
+  if (CONST_POLY_INT_P (src)
+      && is_a <scalar_int_mode> (mode, &int_mode))
+    {
+      poly_int64 value = rtx_to_poly_int64 (src);
+      if (!value.is_constant () && !TARGET_VECTOR)
+	{
+	  rvv_report_required ();
+	  return true;
+	}
+      if (GET_MODE_SIZE (mode).to_constant () < GET_MODE_SIZE (Pmode))
+	{
+	  rtx clobber = gen_reg_rtx (Pmode);
+	  rvv_expand_poly_move (Pmode, gen_lowpart (Pmode, dest), clobber, src);
+	}
+      else
+	{
+	  rtx clobber = gen_reg_rtx (mode);
+	  rvv_expand_poly_move (mode, dest, clobber, src);
+	}
+      return true;
+    }
   /* Expand 
        (set (reg:QI target) (mem:QI (address))) 
      to
@@ -4998,7 +5034,9 @@  riscv_hard_regno_mode_ok (unsigned int regno, machine_mode mode)
 
       if (regsize != 1)
 	return ((regno % regsize) == 0);		
-    }			
+    }
+	else if (regno == VTYPE_REGNUM || regno == VL_REGNUM)
+	  return (nregs == 1 && mode == SImode);
   else
     return false;
 
@@ -5388,7 +5426,7 @@  riscv_conditional_register_usage (void)
   if (!TARGET_VECTOR)
     {
       for (int regno = V_REG_FIRST; regno <= V_REG_LAST; regno++)
-	call_used_regs[regno] = 1;
+	fixed_regs[regno] = call_used_regs[regno] = 1;
 	
       fixed_regs[VTYPE_REGNUM] = call_used_regs[VTYPE_REGNUM] = 1;
       fixed_regs[VL_REGNUM] = call_used_regs[VL_REGNUM] = 1;