@@ -49,6 +49,32 @@ along with GCC; see the file COPYING3. If not see
#include "sched-int.h"
#include "tm-constrs.h"
+/* Scheduler state tracking for dual-pipe ARCV architectures. */
+
+struct arcv_sched_state {
+ /* True if the ALU pipe has been scheduled for the current cycle.
+ The ALU pipe handles arithmetic, logical, and other computational
+ instructions. */
+ int alu_pipe_scheduled_p;
+
+ /* True if pipe B has been scheduled for the current cycle.
+ Pipe B is the second execution pipe, typically used for memory
+ operations (loads/stores) but can also handle other instructions. */
+ int pipeB_scheduled_p;
+
+ /* The last instruction that was scheduled. Used to detect fusion
+ opportunities by looking ahead at the next instruction to be
+ scheduled. */
+ rtx_insn *last_scheduled_insn;
+
+ /* Cached value of how many more instructions can be issued in the
+ current cycle. Updated as instructions are scheduled and pipes
+ become occupied. */
+ short cached_can_issue_more;
+};
+
+static struct arcv_sched_state sched_state;
+
/* If INSN is a load or store of address in the form of [base+offset],
extract the two parts and set to BASE and OFFSET. IS_LOAD is set
to TRUE if it's a load. Return TRUE if INSN is such an instruction,
@@ -160,3 +186,224 @@ arcv_sched_fusion_priority (rtx_insn *insn, int max_pri, int *fusion_pri,
return true;
}
+
+/* Initialize ARCV scheduler state at the beginning of scheduling. */
+
+void
+arcv_sched_init (void)
+{
+ sched_state.last_scheduled_insn = 0;
+}
+
+/* Try to reorder ready queue to promote ARCV fusion opportunities.
+ Returns the number of instructions that can be issued this cycle. */
+
+int
+arcv_sched_reorder2 (rtx_insn **ready, int *n_readyp)
+{
+ if (sched_fusion)
+ return sched_state.cached_can_issue_more;
+
+ if (!sched_state.cached_can_issue_more)
+ return 0;
+
+ /* Fuse double load/store instances missed by sched_fusion. */
+ if (!sched_state.pipeB_scheduled_p && sched_state.last_scheduled_insn
+ && ready && *n_readyp > 0
+ && !SCHED_GROUP_P (sched_state.last_scheduled_insn)
+ && (get_attr_type (sched_state.last_scheduled_insn) == TYPE_LOAD
+ || get_attr_type (sched_state.last_scheduled_insn) == TYPE_STORE))
+ {
+ for (int i = 1; i <= *n_readyp; i++)
+ {
+ rtx_insn *next_insn
+ = next_nonnote_nondebug_insn_bb (ready[*n_readyp - i]);
+ /* Try to fuse the last_scheduled_insn with. */
+ /* Fuse only with nondebug insn. */
+ if (NONDEBUG_INSN_P (ready[*n_readyp - i])
+ /* Which have not been already fused. */
+ && !SCHED_GROUP_P (ready[*n_readyp - i])
+ && (!next_insn || !NONDEBUG_INSN_P (next_insn)
+ || !SCHED_GROUP_P (next_insn))
+ && riscv_macro_fusion_pair_p (sched_state.last_scheduled_insn,
+ ready[*n_readyp - i]))
+ {
+ std::swap (ready[*n_readyp - 1], ready[*n_readyp - i]);
+ SCHED_GROUP_P (ready[*n_readyp - 1]) = 1;
+ sched_state.pipeB_scheduled_p = 1;
+ return sched_state.cached_can_issue_more;
+ }
+ }
+ sched_state.pipeB_scheduled_p = 1;
+ }
+
+ /* Try to fuse a non-memory last_scheduled_insn. */
+ if ((!sched_state.alu_pipe_scheduled_p || !sched_state.pipeB_scheduled_p)
+ && sched_state.last_scheduled_insn && ready && *n_readyp > 0
+ && !SCHED_GROUP_P (sched_state.last_scheduled_insn)
+ && (get_attr_type (sched_state.last_scheduled_insn) != TYPE_LOAD
+ && get_attr_type (sched_state.last_scheduled_insn) != TYPE_STORE))
+ {
+ for (int i = 1; i <= *n_readyp; i++)
+ {
+ rtx_insn* next_insn
+ = next_nonnote_nondebug_insn_bb (ready[*n_readyp - i]);
+ if (NONDEBUG_INSN_P (ready[*n_readyp - i])
+ && !SCHED_GROUP_P (ready[*n_readyp - i])
+ && active_insn_p (ready[*n_readyp - i])
+ && (!next_insn || !NONDEBUG_INSN_P (next_insn)
+ || !SCHED_GROUP_P (next_insn))
+ && riscv_macro_fusion_pair_p (sched_state.last_scheduled_insn,
+ ready[*n_readyp - i]))
+ {
+ if (get_attr_type (ready[*n_readyp - i]) == TYPE_LOAD
+ || get_attr_type (ready[*n_readyp - i]) == TYPE_STORE)
+ {
+ if (sched_state.pipeB_scheduled_p)
+ continue;
+ else
+ sched_state.pipeB_scheduled_p = 1;
+ }
+ else if (!sched_state.alu_pipe_scheduled_p)
+ sched_state.alu_pipe_scheduled_p = 1;
+ else
+ sched_state.pipeB_scheduled_p = 1;
+
+ std::swap (ready[*n_readyp - 1], ready[*n_readyp - i]);
+ SCHED_GROUP_P (ready[*n_readyp - 1]) = 1;
+ return sched_state.cached_can_issue_more;
+ }
+ }
+ sched_state.alu_pipe_scheduled_p = 1;
+ }
+ /* When pipe B is scheduled, we can have no more memops this cycle. */
+ if (sched_state.pipeB_scheduled_p && *n_readyp > 0
+ && NONDEBUG_INSN_P (ready[*n_readyp - 1])
+ && recog_memoized (ready[*n_readyp - 1]) >= 0
+ && !SCHED_GROUP_P (ready[*n_readyp - 1]))
+ {
+ rtx_insn *head_next = next_nonnote_nondebug_insn_bb (ready[*n_readyp - 1]);
+ if (get_attr_type (ready[*n_readyp - 1]) == TYPE_LOAD
+ || get_attr_type (ready[*n_readyp - 1]) == TYPE_STORE
+ || (head_next && NONDEBUG_INSN_P (head_next)
+ && SCHED_GROUP_P (head_next) && recog_memoized (head_next) >= 0
+ && (get_attr_type (head_next) == TYPE_LOAD
+ || get_attr_type (head_next) == TYPE_STORE)))
+ {
+ if (sched_state.alu_pipe_scheduled_p)
+ return 0;
+
+ for (int i = 2; i <= *n_readyp; i++)
+ {
+ rtx_insn* next_insn
+ = next_nonnote_nondebug_insn_bb (ready[*n_readyp - i]);
+ if ((NONDEBUG_INSN_P (ready[*n_readyp - i])
+ && recog_memoized (ready[*n_readyp - i]) >= 0
+ && get_attr_type (ready[*n_readyp - i]) != TYPE_LOAD
+ && get_attr_type (ready[*n_readyp - i]) != TYPE_STORE
+ && !SCHED_GROUP_P (ready[*n_readyp - i])
+ && (!next_insn || !NONDEBUG_INSN_P (next_insn)
+ || !SCHED_GROUP_P (next_insn)))
+ || (next_insn && NONDEBUG_INSN_P (next_insn)
+ && recog_memoized (next_insn) >= 0
+ && get_attr_type (next_insn) != TYPE_LOAD
+ && get_attr_type (next_insn) != TYPE_STORE))
+ {
+ std::swap (ready[*n_readyp - 1], ready[*n_readyp - i]);
+ sched_state.alu_pipe_scheduled_p = 1;
+ sched_state.cached_can_issue_more = 1;
+ return 1;
+ }
+ }
+ return 0;
+ }
+ }
+
+ /* If all else fails, schedule a single (fused) instruction. */
+ if (ready && *n_readyp > 0
+ && NONDEBUG_INSN_P (ready[*n_readyp - 1])
+ && recog_memoized (ready[*n_readyp - 1]) >= 0)
+ {
+ rtx_insn *next_insn = next_nonnote_nondebug_insn_bb (ready[*n_readyp - 1]);
+
+ sched_state.cached_can_issue_more
+ = next_insn && NONDEBUG_INSN_P (next_insn) && SCHED_GROUP_P (next_insn)
+ ? 2 : 1;
+ }
+
+ return sched_state.cached_can_issue_more;
+}
+
+int
+arcv_sched_adjust_priority (rtx_insn *insn, int priority)
+{
+ /* Bump the priority of fused load-store pairs for easier
+ scheduling of the memory pipe. The specific increase
+ value is determined empirically. */
+ rtx_insn *next = next_nonnote_nondebug_insn_bb (insn);
+ if (next && single_set (insn) && single_set (next)
+ && SCHED_GROUP_P (next)
+ && ((get_attr_type (insn) == TYPE_STORE
+ && get_attr_type (next) == TYPE_STORE)
+ || (get_attr_type (insn) == TYPE_LOAD
+ && get_attr_type (next) == TYPE_LOAD)))
+ return priority + 1;
+
+ return priority;
+}
+
+/* Adjust scheduling cost for ARCV fusion. */
+
+int
+arcv_sched_adjust_cost (rtx_insn *insn, int dep_type, int cost)
+{
+ if (dep_type == REG_DEP_ANTI && !SCHED_GROUP_P (insn))
+ return cost + 1;
+
+ return cost;
+}
+
+bool
+arcv_can_issue_more_p (int issue_rate, int more, rtx_insn *insn)
+{
+ /* Beginning of cycle - reset variables. */
+ if (more == issue_rate)
+ {
+ sched_state.alu_pipe_scheduled_p = 0;
+ sched_state.pipeB_scheduled_p = 0;
+ }
+
+ if (!(insn && NONDEBUG_INSN_P (insn) && SCHED_GROUP_P (insn))
+ && sched_state.alu_pipe_scheduled_p && sched_state.pipeB_scheduled_p)
+ {
+ sched_state.cached_can_issue_more = 0;
+ return false;
+ }
+
+ sched_state.cached_can_issue_more = more;
+
+ return true;
+}
+
+int
+arcv_sched_variable_issue (rtx_insn *insn, int more)
+{
+ rtx_insn *next = next_nonnote_nondebug_insn_bb (insn);
+ if (next && NONDEBUG_INSN_P (next) && SCHED_GROUP_P (next)
+ && single_set (insn) && single_set (next))
+ {
+ if (get_attr_type (insn) == TYPE_LOAD
+ || get_attr_type (insn) == TYPE_STORE
+ || get_attr_type (next) == TYPE_LOAD
+ || get_attr_type (next) == TYPE_STORE
+ || sched_state.alu_pipe_scheduled_p)
+ sched_state.pipeB_scheduled_p = 1;
+ else
+ sched_state.alu_pipe_scheduled_p = 1;
+ }
+
+ sched_state.last_scheduled_insn = insn;
+ sched_state.cached_can_issue_more = more - 1;
+
+ return sched_state.cached_can_issue_more;
+}
@@ -917,6 +917,12 @@ extern bool arcv_mpy_10c_bypass_p (rtx_insn *, rtx_insn *);
/* Routines implemented in arcv.cc. */
extern bool arcv_pair_fusion_mode_allowed_p (machine_mode, bool);
extern bool arcv_sched_fusion_priority (rtx_insn *, int, int *, int *);
+extern void arcv_sched_init (void);
+extern int arcv_sched_reorder2 (rtx_insn **, int *);
+extern int arcv_sched_adjust_priority (rtx_insn *, int);
+extern int arcv_sched_adjust_cost (rtx_insn *, int, int);
+extern bool arcv_can_issue_more_p (int, int, rtx_insn *);
+extern int arcv_sched_variable_issue (rtx_insn *, int);
extern bool strided_load_broadcast_p (void);
extern bool riscv_prefer_agnostic_p (void);
@@ -11455,6 +11455,10 @@ struct last_vconfig
rtx avl;
} last_vconfig;
+/* Cached value of can_issue_more. Set by riscv_sched_variable_issue and
+ returned from riscv_sched_reorder2. */
+static int cached_can_issue_more;
+
/* Clear LAST_VCONFIG so we have no known state. */
static void
clear_vconfig (void)
@@ -11507,23 +11511,42 @@ static void
riscv_sched_init (FILE *, int, int)
{
clear_vconfig ();
+
+ if (TARGET_ARCV_RHX100)
+ arcv_sched_init ();
}
/* Implement TARGET_SCHED_VARIABLE_ISSUE. */
static int
riscv_sched_variable_issue (FILE *, int, rtx_insn *insn, int more)
{
+ if (TARGET_ARCV_RHX100)
+ if (!arcv_can_issue_more_p (riscv_issue_rate (), more, insn))
+ {
+ cached_can_issue_more = 0;
+ return 0;
+ }
+
if (DEBUG_INSN_P (insn))
- return more;
+ {
+ cached_can_issue_more = more;
+ return more;
+ }
rtx_code code = GET_CODE (PATTERN (insn));
if (code == USE || code == CLOBBER)
- return more;
+ {
+ cached_can_issue_more = more;
+ return more;
+ }
/* GHOST insns are used for blockage and similar cases which
effectively end a cycle. */
if (get_attr_type (insn) == TYPE_GHOST)
- return 0;
+ {
+ cached_can_issue_more = 0;
+ return 0;
+ }
/* If we ever encounter an insn with an unknown type, trip
an assert so we can find and fix this problem. */
@@ -11558,6 +11581,14 @@ riscv_sched_variable_issue (FILE *, int, rtx_insn *insn, int more)
}
}
+ if (TARGET_ARCV_RHX100)
+ {
+ more = arcv_sched_variable_issue (insn, more);
+ cached_can_issue_more = more;
+ return more;
+ }
+
+ cached_can_issue_more = more - 1;
return more - 1;
}
@@ -11631,9 +11662,12 @@ riscv_get_fusible_ops (void)
we currently only perform the adjustment when -madjust-lmul-cost is given.
*/
static int
-riscv_sched_adjust_cost (rtx_insn *, int, rtx_insn *insn, int cost,
- unsigned int)
+riscv_sched_adjust_cost (rtx_insn *insn, int dep_type, rtx_insn *dep_insn,
+ int cost, unsigned int)
{
+ /* Use ARCV-specific cost adjustment for RHX-100. */
+ if (TARGET_ARCV_RHX100)
+ return arcv_sched_adjust_cost (insn, dep_type, cost);
/* Only do adjustments for the generic out-of-order and spacemit_x60
scheduling model. */
@@ -11642,10 +11676,10 @@ riscv_sched_adjust_cost (rtx_insn *, int, rtx_insn *insn, int cost,
&& riscv_microarchitecture != spacemit_x60))
return cost;
- if (recog_memoized (insn) < 0)
+ if (recog_memoized (dep_insn) < 0)
return cost;
- enum attr_type type = get_attr_type (insn);
+ enum attr_type type = get_attr_type (dep_insn);
if (type == TYPE_VFREDO || type == TYPE_VFWREDO)
{
@@ -11663,7 +11697,7 @@ riscv_sched_adjust_cost (rtx_insn *, int, rtx_insn *insn, int cost,
return cost;
enum riscv_vector::vlmul_type lmul =
- (riscv_vector::vlmul_type)get_attr_vlmul (insn);
+ (riscv_vector::vlmul_type)get_attr_vlmul (dep_insn);
double factor = 1;
switch (lmul)
@@ -11696,6 +11730,32 @@ riscv_sched_adjust_cost (rtx_insn *, int, rtx_insn *insn, int cost,
return new_cost;
}
+/* Implement TARGET_SCHED_ADJUST_PRIORITY hook. */
+
+static int
+riscv_sched_adjust_priority (rtx_insn *insn, int priority)
+{
+ if (TARGET_ARCV_RHX100)
+ return arcv_sched_adjust_priority (insn, priority);
+
+ return priority;
+}
+
+/* Implement TARGET_SCHED_REORDER2 hook. */
+
+static int
+riscv_sched_reorder2 (FILE *file ATTRIBUTE_UNUSED,
+ int verbose ATTRIBUTE_UNUSED,
+ rtx_insn **ready,
+ int *n_readyp,
+ int clock ATTRIBUTE_UNUSED)
+{
+ if (TARGET_ARCV_RHX100)
+ return arcv_sched_reorder2 (ready, n_readyp);
+
+ return cached_can_issue_more;
+}
+
/* Implement TARGET_SCHED_CAN_SPECULATE_INSN hook. Return true if insn
can be scheduled for speculative execution. Reject vsetvl instructions to
prevent the scheduler from hoisting them out of basic blocks without
@@ -16605,6 +16665,12 @@ riscv_memtag_tag_bitsize ()
#undef TARGET_SCHED_ADJUST_COST
#define TARGET_SCHED_ADJUST_COST riscv_sched_adjust_cost
+#undef TARGET_SCHED_ADJUST_PRIORITY
+#define TARGET_SCHED_ADJUST_PRIORITY riscv_sched_adjust_priority
+
+#undef TARGET_SCHED_REORDER2
+#define TARGET_SCHED_REORDER2 riscv_sched_reorder2
+
#undef TARGET_SCHED_CAN_SPECULATE_INSN
#define TARGET_SCHED_CAN_SPECULATE_INSN riscv_sched_can_speculate_insn
@@ -13,5 +13,4 @@ fuse_ls_update_rev (int *p, int n, int val)
}
}
-/* XFAIL until scheduling support for RHX-100 series is implemented. */
-/* { dg-final { scan-rtl-dump "RISCV_FUSE_LS_UPDATE)" "sched2" { xfail *-*-* } } } */
+/* { dg-final { scan-rtl-dump "RISCV_FUSE_LS_UPDATE" "sched2" } } */
@@ -12,5 +12,4 @@ fuse_ls_update (int *p, int n)
return sum;
}
-/* XFAIL until scheduling support for RHX-100 series is implemented. */
-/* { dg-final { scan-rtl-dump "RISCV_FUSE_LS_UPDATE" "sched2" { xfail *-*-* } } } */
+/* { dg-final { scan-rtl-dump "RISCV_FUSE_LS_UPDATE" "sched2" } } */