[PATCH v5 15/18] RISC-V: Add ascending integer load/store pair fusion

Jin Ma <[email protected]>
Newsgroups gmane.comp.gcc.patches
Message-ID <[email protected]>
Recognize adjacent 4-byte or 8-byte integer loads or stores whose
addresses share a base and increase by one access size.  Factor common
pair parsing into a helper reused by ALIGNED_STD, and parameterize the
direction and register-class differences for the remaining pair rules.

Leave the new fusion disabled by default and XFAIL its positive dump
checks until a CPU enables it.

gcc/ChangeLog:

	* config/riscv/riscv-fusion.cc (riscv_fuse_mem_pair_p): New
	function.
	(riscv_fuse_ldst_pair_p): Likewise.
	(riscv_fuse_aligned_std): Use riscv_fuse_mem_pair_p.
	(riscv_fuse_ldst_pair_inc): New function.
	(riscv_fusion_table): Add RISCV_FUSE_LDST_PAIR_INC.
	* config/riscv/riscv-protos.h (enum riscv_fusion_pairs): Add
	RISCV_FUSE_LDST_PAIR_INC.

gcc/testsuite/ChangeLog:

	* gcc.target/riscv/fusion-ldst-pair-inc.c: New test.
	* gcc.target/riscv/fusion-memory-rtl-shapes-ldst-pair.c: Likewise.

Signed-off-by: Jin Ma <[email protected]>
---
 gcc/config/riscv/riscv-fusion.cc              | 109 +++++++++++++++---
 gcc/config/riscv/riscv-protos.h               |   1 +
 .../gcc.target/riscv/fusion-ldst-pair-inc.c   |  33 ++++++
 .../fusion-memory-rtl-shapes-ldst-pair.c      |  33 ++++++
 4 files changed, 162 insertions(+), 14 deletions(-)
 create mode 100644 gcc/testsuite/gcc.target/riscv/fusion-ldst-pair-inc.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/fusion-memory-rtl-shapes-ldst-pair.c

diff --git a/gcc/config/riscv/riscv-fusion.cc b/gcc/config/riscv/riscv-fusion.cc
index 530c24482b2..419628a5038 100644
--- a/gcc/config/riscv/riscv-fusion.cc
+++ b/gcc/config/riscv/riscv-fusion.cc
@@ -839,6 +839,74 @@ riscv_fuse_indexed_mem_p (rtx_insn *prev, rtx_insn *curr,
   return !riscv_fuse_same_reg_p (SET_SRC (mem_set), update_dest);
 }
 
+/* Extract a pair of scalar loads or stores with the same mode and base
+   register.  Store whether they are loads in *LOAD_P.  */
+
+static bool
+riscv_fuse_mem_pair_p (rtx_insn *prev, rtx_insn *curr,
+			 struct riscv_fusion_mem_info *prev_mem,
+			 struct riscv_fusion_mem_info *curr_mem,
+			 bool *load_p)
+{
+  if (!riscv_fuse_sets_p (prev, curr)
+      || !riscv_fuse_mem_p (prev, prev_mem)
+      || !riscv_fuse_mem_p (curr, curr_mem))
+    return false;
+
+  bool prev_load_p = prev_mem->type != SCHED_FUSION_ST;
+  bool curr_load_p = curr_mem->type != SCHED_FUSION_ST;
+  if (prev_load_p != curr_load_p
+      || prev_mem->fp_p != curr_mem->fp_p
+      || prev_mem->mode != curr_mem->mode
+      || prev_mem->addr.type != ADDRESS_REG
+      || curr_mem->addr.type != ADDRESS_REG
+      || !CONST_INT_P (prev_mem->addr.offset)
+      || !CONST_INT_P (curr_mem->addr.offset)
+      || !riscv_fuse_same_reg_p (prev_mem->addr.reg,
+				   curr_mem->addr.reg))
+    return false;
+
+  *load_p = prev_load_p;
+  return true;
+}
+
+/* Check common adjacent load/store-pair constraints.  INC_P selects ascending
+   offsets and FP_P selects floating-point rather than integer accesses.  */
+
+static bool
+riscv_fuse_ldst_pair_p (rtx_insn *prev, rtx_insn *curr,
+			  bool inc_p, bool fp_p)
+{
+  struct riscv_fusion_mem_info prev_mem, curr_mem;
+  bool load_p;
+  if (!riscv_fuse_mem_pair_p (prev, curr, &prev_mem, &curr_mem,
+				&load_p)
+      || prev_mem.fp_p != fp_p
+      || prev_mem.type == SCHED_FUSION_LD_ZERO_EXTEND
+      || curr_mem.type == SCHED_FUSION_LD_ZERO_EXTEND)
+    return false;
+
+  HOST_WIDE_INT access_size = GET_MODE_SIZE (prev_mem.mode).to_constant ();
+  if (access_size != 4 && access_size != 8)
+    return false;
+
+  if (load_p)
+    {
+      rtx prev_dest = SET_DEST (single_set (prev));
+      rtx curr_dest = SET_DEST (single_set (curr));
+      if (riscv_fuse_same_reg_p (prev_dest, curr_dest)
+	  || riscv_fuse_same_reg_p (prev_mem.addr.reg, prev_dest))
+	return false;
+    }
+
+  HOST_WIDE_INT diff = inc_p
+		       ? INTVAL (curr_mem.addr.offset)
+			 - INTVAL (prev_mem.addr.offset)
+		       : INTVAL (prev_mem.addr.offset)
+			 - INTVAL (curr_mem.addr.offset);
+  return diff == access_size;
+}
+
 /* Check the common RTL for ZEXTW, ZEXTWS and ZEXTH fusion.  */
 
 static bool
@@ -1422,22 +1490,13 @@ riscv_fuse_auipc_ld (rtx_insn *prev, rtx_insn *curr)
 static bool
 riscv_fuse_aligned_std (rtx_insn *prev, rtx_insn *curr)
 {
-  if (!riscv_fuse_sets_p (prev, curr))
-    return false;
-
   struct riscv_fusion_mem_info prev_mem, curr_mem;
-
-  if (!riscv_fuse_mem_p (prev, &prev_mem)
-      || !riscv_fuse_mem_p (curr, &curr_mem)
-      || prev_mem.type != SCHED_FUSION_ST
-      || curr_mem.type != SCHED_FUSION_ST
+  bool load_p;
+  if (!riscv_fuse_mem_pair_p (prev, curr, &prev_mem, &curr_mem,
+				&load_p)
+      || load_p
       || prev_mem.fp_p
-      || curr_mem.fp_p
-      || !SCALAR_INT_MODE_P (prev_mem.mode)
-      || prev_mem.mode != curr_mem.mode
-      || prev_mem.addr.type != ADDRESS_REG
-      || curr_mem.addr.type != ADDRESS_REG
-      || !riscv_fuse_same_reg_p (prev_mem.addr.reg, curr_mem.addr.reg))
+      || !SCALAR_INT_MODE_P (prev_mem.mode))
     return false;
 
   unsigned int mode_size
@@ -1454,6 +1513,26 @@ riscv_fuse_aligned_std (rtx_insn *prev, rtx_insn *curr)
   return false;
 }
 
+/* Check for RISCV_FUSE_LDST_PAIR_INC fusion.
+   prev/curr (one of the following pairs):
+     prev (lw/ld) == (set (reg rd1) (mem (rs1, offset1)))
+     curr (lw/ld) == (set (reg rd2) (mem (rs1, offset2)))
+
+     prev (sw/sd) == (set (mem (rs1, offset1)) (reg rs2))
+     curr (sw/sd) == (set (mem (rs1, offset2)) (reg rs3))
+
+   Constraints:
+     access size is 4 or 8 bytes
+     offset2 - offset1 equals the access size
+     loads are not zero-extending
+     for loads, rd1 != rd2 and rd1 != rs1.  */
+
+static bool
+riscv_fuse_ldst_pair_inc (rtx_insn *prev, rtx_insn *curr)
+{
+  return riscv_fuse_ldst_pair_p (prev, curr, true, false);
+}
+
 /* Check for RISCV_FUSE_BFEXT fusion.
    prev (slli) == (set (reg rd1)
 		       (ashift (reg rs1) (const_int shamt1)))
@@ -1797,6 +1876,8 @@ static const struct riscv_fusion_entry riscv_fusion_table[] =
     riscv_fuse_auipc_ld, "RISCV_FUSE_AUIPC_LD" },
   { RISCV_FUSE_ALIGNED_STD,
     riscv_fuse_aligned_std, "RISCV_FUSE_ALIGNED_STD" },
+  { RISCV_FUSE_LDST_PAIR_INC,
+    riscv_fuse_ldst_pair_inc, "RISCV_FUSE_LDST_PAIR_INC" },
   { RISCV_FUSE_BFEXT,
     riscv_fuse_bfext, "RISCV_FUSE_BFEXT" },
   { RISCV_FUSE_SLLI_SRLI,
diff --git a/gcc/config/riscv/riscv-protos.h b/gcc/config/riscv/riscv-protos.h
index 445d1f7f257..de4476a2d66 100644
--- a/gcc/config/riscv/riscv-protos.h
+++ b/gcc/config/riscv/riscv-protos.h
@@ -876,6 +876,7 @@ enum riscv_fusion_pairs
   RISCV_FUSE_PREINDEX_ST = HOST_WIDE_INT_1U << 20,
   RISCV_FUSE_POSTINDEX_LD = HOST_WIDE_INT_1U << 21,
   RISCV_FUSE_POSTINDEX_ST = HOST_WIDE_INT_1U << 22,
+  RISCV_FUSE_LDST_PAIR_INC = HOST_WIDE_INT_1U << 23,
 };
 
 extern bool riscv_macro_fusion_p (void);
diff --git a/gcc/testsuite/gcc.target/riscv/fusion-ldst-pair-inc.c b/gcc/testsuite/gcc.target/riscv/fusion-ldst-pair-inc.c
new file mode 100644
index 00000000000..44ad262bf17
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/fusion-ldst-pair-inc.c
@@ -0,0 +1,33 @@
+/* { dg-do compile { target { rv64 } } } */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-O3" "-O[sgz]" "-flto" } } */
+/* { dg-options "-march=rv64gc -mabi=lp64d -mtune=xt-c9501fdvt -O2 -fdump-rtl-sched2-details" } */
+/* { dg-final { scan-rtl-dump-times "RISCV_FUSE_LDST_PAIR_INC" 4 "sched2" { xfail *-*-* } } } */
+
+typedef int int32_t;
+typedef long int64_t;
+
+int64_t
+test_ld_pair_inc (int64_t *p)
+{
+  return p[0] + p[1];
+}
+
+int64_t
+test_lw_pair_inc (int32_t *p)
+{
+  return (int64_t) p[0] + p[1];
+}
+
+void
+test_sd_pair_inc (int64_t *p, int64_t a, int64_t b)
+{
+  p[0] = a;
+  p[1] = b;
+}
+
+void
+test_sw_pair_inc (int32_t *p, int32_t a, int32_t b)
+{
+  p[0] = a;
+  p[1] = b;
+}
diff --git a/gcc/testsuite/gcc.target/riscv/fusion-memory-rtl-shapes-ldst-pair.c b/gcc/testsuite/gcc.target/riscv/fusion-memory-rtl-shapes-ldst-pair.c
new file mode 100644
index 00000000000..2a19c6f3d97
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/fusion-memory-rtl-shapes-ldst-pair.c
@@ -0,0 +1,33 @@
+/* { dg-do compile { target { rv64 } } } */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-O3" "-O[sgz]" "-flto" } } */
+/* { dg-options "-march=rv64gc -mabi=lp64d -O2 -mtune=xt-c9501fdvt -fdump-rtl-sched2-details" } */
+/* { dg-final { scan-rtl-dump-times "RISCV_FUSE_LDST_PAIR_INC" 1 "sched2" { xfail *-*-* } } } */
+
+/* The second load destination may be the shared base register.  */
+long __RTL (startwith ("sched2"))
+test_load_pair_inc_second_destination_is_base (void)
+{
+(function "test_load_pair_inc_second_destination_is_base"
+  (insn-chain
+    (block 2
+      (edge-from entry (flags "FALLTHRU"))
+      (cnote 1 [bb 2] NOTE_INSN_BASIC_BLOCK)
+      (cnote 2 NOTE_INSN_FUNCTION_BEG)
+      (cinsn 3 (set (reg:DI a1)
+                    (mem:DI (reg:DI a0) [0 S8 A64])))
+      (cinsn 4 (set (reg:DI a0)
+                    (mem:DI
+                      (plus:DI (reg:DI a0)
+                               (const_int 8)) [0 S8 A64])))
+      (cinsn 5 (use (reg:DI a1)))
+      (cinsn 6 (use (reg:DI a0)))
+      (cjump_insn 7 (simple_return))
+      (edge-to exit)
+    ) ;; block 2
+    (cbarrier 8)
+  ) ;; insn-chain
+  (crtl
+    (return_rtx (reg/i:DI a0))
+  ) ;; crtl
+) ;; function "test_load_pair_inc_second_destination_is_base"
+}
-- 
2.52.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.