Factor common RTL matching out of the existing fusion recognizers and
use it to validate register dependencies consistently. Restrict ADDI
matching to immediates encodable by the instruction.
gcc/ChangeLog:
* config/riscv/riscv-fusion.cc (riscv_set_extract_word_add_p):
New function.
(riscv_regno): Likewise.
(riscv_fusion_same_dest_p): Handle hard-register SUBREGs and
optionally check the first-source dependency.
(riscv_set_is_addi_p): Check the immediate range.
(riscv_fuse_ldpreincrement): Check the update dependency.
(riscv_fuse_lui_addi): Reuse the ADDI matcher, check the source
dependency, and reject x0 and zero-valued LUI patterns.
(riscv_fuse_auipc_addi): Check the source dependency and reject x0.
(riscv_fuse_auipc_ld): Check the address base dependency.
(riscv_fuse_bfext): Use riscv_regno to validate the source register.
gcc/testsuite/ChangeLog:
* gcc.target/riscv/fusion-lui-addi-boundary.c: New test.
* gcc.target/riscv/fusion-lui-addi-dependency-rtl.c: New test.
---
gcc/config/riscv/riscv-fusion.cc | 154 +++++++++++++++---
.../riscv/fusion-lui-addi-boundary.c | 61 +++++++
.../riscv/fusion-lui-addi-dependency-rtl.c | 122 ++++++++++++++
3 files changed, 316 insertions(+), 21 deletions(-)
create mode 100644 gcc/testsuite/gcc.target/riscv/fusion-lui-addi-boundary.c
create mode 100644
gcc/testsuite/gcc.target/riscv/fusion-lui-addi-dependency-rtl.c
diff --git a/gcc/config/riscv/riscv-fusion.cc b/gcc/config/riscv/riscv-fusion.cc
index f276ab42e18..dd58661df30 100644
--- a/gcc/config/riscv/riscv-fusion.cc
+++ b/gcc/config/riscv/riscv-fusion.cc
@@ -51,25 +51,128 @@ riscv_fusion_enabled_p (enum riscv_fusion_pairs op)
return riscv_get_fusible_ops () & op;
}
-/* Return true if PREV_SET and CURR_SET satisfy the same-dest constraint
- required by most fusion rules: when we are past register allocation
- (i.e. can_create_pseudo_p () is false), the destination registers of
- the two sets must be the same physical register. */
+/* Matches a word add:
+ (set (reg:DI rd)
+ (sign_extend:DI (plus:SI (reg:SI rs1) (reg:SI rs2))))
+ or
+ (set (reg:DI rd)
+ (sign_extend:DI (truncate:SI (plus:DI (reg:DI rs1) (reg:DI rs2)))))
+ or
+ (set (reg:DI rd)
+ (sign_extend:DI (subreg:SI (plus:DI (reg:DI rs1) (reg:DI rs2)) 0)))
+ Store the PLUS in *PLUS_SRC. */
static bool
-riscv_fusion_same_dest_p (rtx prev_set, rtx curr_set)
+riscv_set_extract_word_add_p (rtx set, rtx *plus_src)
{
- if (can_create_pseudo_p ())
- return true;
+ rtx src = SET_SRC (set);
+ *plus_src = NULL_RTX;
+
+ if (!TARGET_64BIT
+ || GET_CODE (src) != SIGN_EXTEND
+ || GET_MODE (src) != DImode)
+ return false;
+
+ src = XEXP (src, 0);
+ if (GET_CODE (src) == PLUS && GET_MODE (src) == SImode)
+ {
+ *plus_src = src;
+ return true;
+ }
+
+ if (GET_MODE (src) != SImode)
+ return false;
+
+ if (GET_CODE (src) == TRUNCATE)
+ src = XEXP (src, 0);
+ else if (SUBREG_P (src) && subreg_lowpart_p (src))
+ src = SUBREG_REG (src);
+ else
+ return false;
+
+ if (GET_CODE (src) == PLUS && GET_MODE (src) == DImode)
+ {
+ *plus_src = src;
+ return true;
+ }
+
+ return false;
+}
- unsigned int prev_dest_regno = (REG_P (SET_DEST (prev_set))
- ? REGNO (SET_DEST (prev_set))
- : FIRST_PSEUDO_REGISTER);
- unsigned int curr_dest_regno = (REG_P (SET_DEST (curr_set))
- ? REGNO (SET_DEST (curr_set))
- : FIRST_PSEUDO_REGISTER);
+/* Return a comparable register number for X, accounting for hard-register
+ SUBREG offsets, or INVALID_REGNUM. */
- return prev_dest_regno == curr_dest_regno;
+static unsigned int
+riscv_regno (rtx x)
+{
+ int regno = true_regnum (x);
+ if (regno >= 0)
+ return regno;
+
+ /* An unassigned pseudo has no hard-register mapping yet. Its lowpart can
+ use the pseudo's identity, but other parts cannot. */
+ if (SUBREG_P (x)
+ && REG_P (SUBREG_REG (x))
+ && !HARD_REGISTER_P (SUBREG_REG (x))
+ && subreg_lowpart_p (x))
+ return REGNO (SUBREG_REG (x));
+
+ return INVALID_REGNUM;
+}
+
+/* Return true if PREV_SET and CURR_SET satisfy the same-destination
+ constraint. Require fixed hard-register destinations to match; otherwise
+ defer matching until after allocation. If USED_IN_SRC_P, also require
+ CURR_SET to use PREV_SET's destination as its first source operand. */
+
+static bool
+riscv_fusion_same_dest_p (rtx prev_set, rtx curr_set,
+ bool used_in_src_p = false)
+{
+ rtx prev_dest = SET_DEST (prev_set);
+ rtx curr_dest = SET_DEST (curr_set);
+ if (!REG_P (prev_dest) || !REG_P (curr_dest))
+ return false;
+
+ unsigned int prev_dest_regno = riscv_regno (prev_dest);
+ unsigned int curr_dest_regno = riscv_regno (curr_dest);
+ if (prev_dest_regno == INVALID_REGNUM
+ || curr_dest_regno == INVALID_REGNUM)
+ return false;
+
+ if (used_in_src_p)
+ {
+ rtx src = SET_SRC (curr_set);
+ rtx word_add_src;
+ if (riscv_set_extract_word_add_p (curr_set, &word_add_src))
+ src = XEXP (word_add_src, 0);
+ else
+ {
+ while (GET_CODE (src) == SIGN_EXTEND
+ || GET_CODE (src) == ZERO_EXTEND)
+ src = XEXP (src, 0);
+
+ if (GET_CODE (src) == NOT
+ || BINARY_P (src)
+ || GET_CODE (src) == LO_SUM
+ || GET_CODE (src) == ZERO_EXTRACT)
+ src = XEXP (src, 0);
+
+ while (GET_CODE (src) == SIGN_EXTEND
+ || GET_CODE (src) == ZERO_EXTEND)
+ src = XEXP (src, 0);
+ }
+
+ if (riscv_regno (src) != prev_dest_regno)
+ return false;
+ }
+
+ /* Hard-register destinations cannot be changed by allocation. */
+ if (!can_create_pseudo_p ()
+ || (HARD_REGISTER_P (prev_dest) && HARD_REGISTER_P (curr_dest)))
+ return prev_dest_regno == curr_dest_regno;
+
+ return true;
}
/* Matches an add:
@@ -93,6 +196,7 @@ riscv_set_is_addi_p (rtx set)
return (GET_CODE (SET_SRC (set)) == PLUS
&& REG_P (XEXP (SET_SRC (set), 0))
&& CONST_INT_P (XEXP (SET_SRC (set), 1))
+ && SMALL_OPERAND (INTVAL (XEXP (SET_SRC (set), 1)))
&& REG_P (SET_DEST (set)));
}
@@ -402,6 +506,8 @@ riscv_fuse_ldpreincrement (rtx_insn *prev, rtx_insn *curr)
&& REGNO (XEXP (SET_SRC (curr_set), 0)) == REGNO (SET_DEST (prev_set))
&& GET_CODE (SET_SRC (prev_set)) == PLUS
&& REG_P (XEXP (SET_SRC (prev_set), 0))
+ && (REGNO (XEXP (SET_SRC (prev_set), 0))
+ == REGNO (SET_DEST (prev_set)))
&& CONST_INT_P (XEXP (SET_SRC (prev_set), 1)))
return true;
@@ -431,15 +537,16 @@ riscv_fuse_lui_addi (rtx_insn *prev, rtx_insn *curr)
if (!prev_set || !curr_set || any_condjump_p (curr))
return false;
- if (!riscv_fusion_same_dest_p (prev_set, curr_set))
+ if (!riscv_fusion_same_dest_p (prev_set, curr_set, true)
+ || REGNO (SET_DEST (prev_set)) == GP_REG_FIRST)
return false;
if ((GET_CODE (SET_SRC (curr_set)) == LO_SUM
- || (GET_CODE (SET_SRC (curr_set)) == PLUS
- && CONST_INT_P (XEXP (SET_SRC (curr_set), 1))
- && SMALL_OPERAND (INTVAL (XEXP (SET_SRC (curr_set), 1)))))
+ || riscv_set_is_addi_p (curr_set))
&& (GET_CODE (SET_SRC (prev_set)) == HIGH
|| (CONST_INT_P (SET_SRC (prev_set))
+ && (SET_SRC (prev_set)
+ != CONST0_RTX (GET_MODE (SET_DEST (prev_set))))
&& LUI_OPERAND (INTVAL (SET_SRC (prev_set))))))
return true;
@@ -466,7 +573,8 @@ riscv_fuse_auipc_addi (rtx_insn *prev, rtx_insn *curr)
if (!prev_set || !curr_set || any_condjump_p (curr))
return false;
- if (!riscv_fusion_same_dest_p (prev_set, curr_set))
+ if (!riscv_fusion_same_dest_p (prev_set, curr_set, true)
+ || REGNO (SET_DEST (prev_set)) == GP_REG_FIRST)
return false;
if (GET_CODE (SET_SRC (prev_set)) == UNSPEC
@@ -565,7 +673,10 @@ riscv_fuse_auipc_ld (rtx_insn *prev, rtx_insn *curr)
&& XINT (SET_SRC (prev_set), 1) == UNSPEC_AUIPC
&& MEM_P (SET_SRC (curr_set))
&& SCALAR_INT_MODE_P (GET_MODE (SET_DEST (curr_set)))
- && GET_CODE (XEXP (SET_SRC (curr_set), 0)) == PLUS)
+ && GET_CODE (XEXP (SET_SRC (curr_set), 0)) == PLUS
+ && REG_P (XEXP (XEXP (SET_SRC (curr_set), 0), 0))
+ && (REGNO (XEXP (XEXP (SET_SRC (curr_set), 0), 0))
+ == REGNO (SET_DEST (prev_set))))
return true;
return false;
@@ -724,7 +835,8 @@ riscv_fuse_bfext (rtx_insn *prev, rtx_insn *curr)
|| GET_CODE (SET_SRC (curr_set)) == ASHIFTRT)
&& REG_P (SET_DEST (prev_set))
&& REG_P (SET_DEST (curr_set))
- && REGNO (XEXP (SET_SRC (curr_set), 0)) == REGNO (SET_DEST (prev_set))
+ && (riscv_regno (XEXP (SET_SRC (curr_set), 0))
+ == REGNO (SET_DEST (prev_set)))
&& CONST_INT_P (XEXP (SET_SRC (prev_set), 1))
&& CONST_INT_P (XEXP (SET_SRC (curr_set), 1)))
return true;
diff --git a/gcc/testsuite/gcc.target/riscv/fusion-lui-addi-boundary.c
b/gcc/testsuite/gcc.target/riscv/fusion-lui-addi-boundary.c
new file mode 100644
index 00000000000..872748dae71
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/fusion-lui-addi-boundary.c
@@ -0,0 +1,61 @@
+/* Check encodable ADDI boundary values through LUI/ADDI fusion. */
+/* { dg-do compile { target { rv64 } } } */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-O3" "-O[sgz]" "-flto" } } */
+/* { dg-options "-march=rv64gc -mabi=lp64d -O2 -fno-dce
-mtune=sifive-p600-series -fdump-rtl-sched1-details" } */
+/* { dg-final { scan-rtl-dump-times "RISCV_FUSE_LUI_ADDI" 2 "sched1" } } */
+
+long __RTL (startwith ("sched1"))
+test_addi_min (void)
+{
+(function "test_addi_min"
+ (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 a0)
+ (const_int 305418240)))
+ (cinsn 4 (set (reg:DI a0)
+ (plus:DI (reg:DI a0)
+ (const_int -2048))))
+ (cinsn 5 (use (reg/i:DI a0)))
+ (cjump_insn 6 (simple_return))
+ (edge-to exit)
+ ) ;; block 2
+ (cbarrier 7)
+ ) ;; insn-chain
+ (crtl
+ (return_rtx
+ (reg/i:DI a0)
+ ) ;; return_rtx
+ ) ;; crtl
+) ;; function "test_addi_min"
+}
+
+long __RTL (startwith ("sched1"))
+test_addi_max (void)
+{
+(function "test_addi_max"
+ (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 a0)
+ (const_int 305418240)))
+ (cinsn 4 (set (reg:DI a0)
+ (plus:DI (reg:DI a0)
+ (const_int 2047))))
+ (cinsn 5 (use (reg/i:DI a0)))
+ (cjump_insn 6 (simple_return))
+ (edge-to exit)
+ ) ;; block 2
+ (cbarrier 7)
+ ) ;; insn-chain
+ (crtl
+ (return_rtx
+ (reg/i:DI a0)
+ ) ;; return_rtx
+ ) ;; crtl
+) ;; function "test_addi_max"
+}
diff --git a/gcc/testsuite/gcc.target/riscv/fusion-lui-addi-dependency-rtl.c
b/gcc/testsuite/gcc.target/riscv/fusion-lui-addi-dependency-rtl.c
new file mode 100644
index 00000000000..b407dc6a813
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/fusion-lui-addi-dependency-rtl.c
@@ -0,0 +1,122 @@
+/* Check that LUI/ADDI fusion requires a true data dependency. */
+/* { dg-do compile { target { rv64 } } } */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-O3" "-O[sgz]" "-flto" } } */
+/* { dg-options "-march=rv64gc -mabi=lp64d -O2 -fno-dce -mcmodel=medany
-mexplicit-relocs -mtune=sifive-p600-series -fdump-rtl-sched1-details" } */
+/* { dg-final { scan-rtl-dump-not "RISCV_FUSE_LUI_ADDI" "sched1" } } */
+/* { dg-final { scan-rtl-dump-not "RISCV_FUSE_AUIPC_ADDI" "sched1" } } */
+
+long __RTL (startwith ("sched1"))
+test_lui_addi_source_mismatch (void)
+{
+(function "test_lui_addi_source_mismatch"
+ (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 a0)
+ (const_int 305418240)))
+ (cinsn 4 (set (reg:DI a0)
+ (plus:DI (reg:DI a1)
+ (const_int 1656))))
+ (cinsn 5 (use (reg:DI a0)))
+ (cjump_insn 6 (simple_return))
+ (edge-to exit)
+ ) ;; block 2
+ (cbarrier 7)
+ ) ;; insn-chain
+ (crtl
+ (return_rtx
+ (reg/i:DI a0)
+ ) ;; return_rtx
+ ) ;; crtl
+) ;; function "test_lui_addi_source_mismatch"
+}
+
+long __RTL (startwith ("sched1"))
+test_auipc_addi_destination_mismatch (void)
+{
+(function "test_auipc_addi_destination_mismatch"
+ (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 a2)
+ (unspec:DI [
+ (symbol_ref:DI ("foo_destination"))
+ (const_int 1)
+ (pc)
+ ] UNSPEC_AUIPC)))
+ (cinsn 4 (set (reg:DI a0)
+ (lo_sum:DI (reg:DI a2)
+ (symbol_ref:DI ("foo_destination")))))
+ (cinsn 5 (use (reg:DI a0)))
+ (cinsn 6 (use (reg:DI a2)))
+ (cjump_insn 7 (simple_return))
+ (edge-to exit)
+ ) ;; block 2
+ (cbarrier 8)
+ ) ;; insn-chain
+ (crtl
+ (return_rtx
+ (reg/i:DI a0)
+ ) ;; return_rtx
+ ) ;; crtl
+) ;; function "test_auipc_addi_destination_mismatch"
+}
+
+long __RTL (startwith ("sched1"))
+test_lui_addi_zero_destination (void)
+{
+(function "test_lui_addi_zero_destination"
+ (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 zero)
+ (const_int 305418240)))
+ (cinsn 4 (set (reg:DI zero)
+ (plus:DI (reg:DI zero)
+ (const_int 1656))))
+ (cjump_insn 5 (simple_return))
+ (edge-to exit)
+ ) ;; block 2
+ (cbarrier 6)
+ ) ;; insn-chain
+ (crtl
+ (return_rtx
+ (reg/i:DI a0)
+ ) ;; return_rtx
+ ) ;; crtl
+) ;; function "test_lui_addi_zero_destination"
+}
+
+long __RTL (startwith ("sched1"))
+test_lui_addi_zero_constant (void)
+{
+(function "test_lui_addi_zero_constant"
+ (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 a0)
+ (const_int 0)))
+ (cinsn 4 (set (reg:DI a0)
+ (plus:DI (reg:DI a0)
+ (const_int 1))))
+ (cinsn 5 (use (reg:DI a0)))
+ (cjump_insn 6 (simple_return))
+ (edge-to exit)
+ ) ;; block 2
+ (cbarrier 7)
+ ) ;; insn-chain
+ (crtl
+ (return_rtx
+ (reg/i:DI a0)
+ ) ;; return_rtx
+ ) ;; crtl
+) ;; function "test_lui_addi_zero_constant"
+}
--
2.52.0