From: juewang <[email protected]>
seq_cost treats every instruction in a replacement sequence as serial.
This can overestimate simple register-only sequences on superscalar
targets and reject profitable if-conversion.
Build dependency levels for simple, single-cycle register operations.
Cost each level using the target reassociation widths for integer,
floating-point and vector operations, with the issue rate as a global
limit. Fall back to the serial cost for unsupported sequences and size
optimization.
gcc/ChangeLog:
* ifcvt.cc (noce_parallel_cost_node): New.
(noce_parallel_cost_class): New.
(noce_parallel_cost_level): New.
(noce_reassociation_code): New.
(noce_parallel_operand_class_p): New.
(noce_parallel_cost_info): New.
(noce_parallel_seq_cost): New.
(default_noce_conversion_profitable_p): Use it.
---
gcc/ifcvt.cc | 251 ++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 250 insertions(+), 1 deletion(-)
diff --git a/gcc/ifcvt.cc b/gcc/ifcvt.cc
index 5ea25f8fbe7..33120a30e7d 100644
--- a/gcc/ifcvt.cc
+++ b/gcc/ifcvt.cc
@@ -803,6 +803,255 @@ noce_reversed_cond_code (struct noce_if_info *if_info)
return reversed_comparison_code (if_info->cond, if_info->jump);
}
+/* A destination and its dependency level in a noce sequence. */
+
+struct noce_parallel_cost_node
+{
+ rtx dest;
+ unsigned int level;
+};
+
+enum noce_parallel_cost_class
+{
+ NOCE_PARALLEL_INT,
+ NOCE_PARALLEL_FP,
+ NOCE_PARALLEL_VECTOR,
+ NOCE_PARALLEL_NUM_CLASSES
+};
+
+/* Instruction counts and widths for one dependency level. */
+
+struct noce_parallel_cost_level
+{
+ unsigned int insns[NOCE_PARALLEL_NUM_CLASSES];
+ unsigned int widths[NOCE_PARALLEL_NUM_CLASSES];
+};
+
+/* Return a tree code corresponding to the operation in SRC. */
+
+static enum tree_code
+noce_reassociation_code (rtx src)
+{
+ switch (GET_CODE (src))
+ {
+ case PLUS:
+ return PLUS_EXPR;
+ case MINUS:
+ return MINUS_EXPR;
+ case MULT:
+ return MULT_EXPR;
+ case AND:
+ return BIT_AND_EXPR;
+ case IOR:
+ return BIT_IOR_EXPR;
+ case XOR:
+ return BIT_XOR_EXPR;
+ case ASHIFT:
+ return LSHIFT_EXPR;
+ case ASHIFTRT:
+ case LSHIFTRT:
+ return RSHIFT_EXPR;
+ case NEG:
+ return NEGATE_EXPR;
+ case IF_THEN_ELSE:
+ return COND_EXPR;
+ case EQ:
+ return EQ_EXPR;
+ case NE:
+ return NE_EXPR;
+ case LT:
+ case LTU:
+ return LT_EXPR;
+ case LE:
+ case LEU:
+ return LE_EXPR;
+ case GT:
+ case GTU:
+ return GT_EXPR;
+ case GE:
+ case GEU:
+ return GE_EXPR;
+ case SMIN:
+ case UMIN:
+ return MIN_EXPR;
+ case SMAX:
+ case UMAX:
+ return MAX_EXPR;
+ default:
+ return NOP_EXPR;
+ }
+}
+
+/* Return true if the modes in X belong to COST_CLASS. */
+
+static bool
+noce_parallel_operand_class_p (rtx x,
+ noce_parallel_cost_class cost_class)
+{
+ subrtx_iterator::array_type array;
+ FOR_EACH_SUBRTX (iter, array, x, ALL)
+ {
+ machine_mode mode = GET_MODE (*iter);
+ if (mode == VOIDmode || GET_MODE_CLASS (mode) == MODE_CC)
+ continue;
+
+ noce_parallel_cost_class operand_class;
+ if (VECTOR_MODE_P (mode))
+ operand_class = NOCE_PARALLEL_VECTOR;
+ else if (FLOAT_MODE_P (mode))
+ operand_class = NOCE_PARALLEL_FP;
+ else if (SCALAR_INT_MODE_P (mode))
+ operand_class = NOCE_PARALLEL_INT;
+ else
+ return false;
+
+ if (operand_class != cost_class)
+ return false;
+ }
+
+ return true;
+}
+
+/* Classify SET for the parallel cost estimate. */
+
+static bool
+noce_parallel_cost_info (rtx set, noce_parallel_cost_class *cost_class,
+ machine_mode *mode, enum tree_code *opcode)
+{
+ rtx dest = SET_DEST (set);
+ rtx src = SET_SRC (set);
+ machine_mode operation_mode = GET_MODE (dest);
+ bool comparison_p = COMPARISON_P (src) || GET_CODE (src) == COMPARE;
+
+ /* Classify comparisons by their input mode. Store-flag operations use
+ the result mode. */
+ if (comparison_p)
+ {
+ machine_mode input_mode = GET_MODE (XEXP (src, 0));
+ if (input_mode != VOIDmode
+ && GET_MODE_CLASS (input_mode) != MODE_CC)
+ operation_mode = input_mode;
+ }
+
+ if (VECTOR_MODE_P (operation_mode))
+ *cost_class = NOCE_PARALLEL_VECTOR;
+ else if (FLOAT_MODE_P (operation_mode))
+ *cost_class = NOCE_PARALLEL_FP;
+ else if (SCALAR_INT_MODE_P (operation_mode))
+ *cost_class = NOCE_PARALLEL_INT;
+ else
+ return false;
+
+ /* Check only the selected values of a conditional select. Reject other
+ operations that cross execution classes. */
+ if (!comparison_p && GET_CODE (src) == IF_THEN_ELSE)
+ {
+ if (!noce_parallel_operand_class_p (XEXP (src, 1), *cost_class)
+ || !noce_parallel_operand_class_p (XEXP (src, 2), *cost_class))
+ return false;
+ }
+ else if (!comparison_p
+ && !noce_parallel_operand_class_p (src, *cost_class))
+ return false;
+
+ *mode = operation_mode;
+ *opcode = noce_reassociation_code (src);
+ return true;
+}
+
+/* Estimate the cost of independent, single-cycle register operations in
+ SEQ using target reassociation widths. */
+
+static unsigned int
+noce_parallel_seq_cost (rtx_insn *seq, bool speed_p)
+{
+ unsigned int serial_cost = seq_cost (seq, speed_p);
+
+ if (!speed_p)
+ return serial_cost;
+
+ auto_vec<noce_parallel_cost_node> nodes;
+ auto_vec<noce_parallel_cost_level> levels;
+
+ /* Build register dependency levels. Place an instruction after its
+ deepest producer; independent instructions remain at the same level. */
+
+ for (rtx_insn *insn = seq; insn; insn = NEXT_INSN (insn))
+ {
+ if (!NONDEBUG_INSN_P (insn))
+ continue;
+
+ rtx set = single_set (insn);
+ if (!set)
+ return serial_cost;
+
+ rtx dest = SET_DEST (set);
+ rtx src = SET_SRC (set);
+
+ if (!REG_P (dest)
+ || contains_mem_rtx_p (src)
+ || side_effects_p (src)
+ || may_trap_p (src)
+ || set_rtx_cost (set, speed_p) != COSTS_N_INSNS (1))
+ return serial_cost;
+
+ unsigned int level = 0;
+ for (unsigned int i = 0; i < nodes.length (); ++i)
+ if (reg_overlap_mentioned_p (nodes[i].dest, src))
+ level = MAX (level, nodes[i].level + 1);
+
+ noce_parallel_cost_class cost_class;
+ machine_mode mode;
+ enum tree_code opcode;
+ if (!noce_parallel_cost_info (set, &cost_class, &mode, &opcode))
+ return serial_cost;
+
+ int width = targetm.sched.reassociation_width (opcode, mode);
+ if (width <= 0)
+ return serial_cost;
+
+ if (levels.length () <= level)
+ levels.safe_grow_cleared (level + 1, true);
+ noce_parallel_cost_level &level_info = levels[level];
+ ++level_info.insns[cost_class];
+ if (level_info.widths[cost_class] == 0)
+ level_info.widths[cost_class] = width;
+ else
+ level_info.widths[cost_class]
+ = MIN (level_info.widths[cost_class], (unsigned int) width);
+
+ noce_parallel_cost_node node = { dest, level };
+ nodes.safe_push (node);
+ }
+
+ unsigned int issue_rate = 1;
+ if (targetm.sched.issue_rate)
+ issue_rate = MAX (targetm.sched.issue_rate (), 1);
+
+ /* Sum the issue cycles needed for each dependency level. */
+
+ unsigned int parallel_cost = 0;
+ for (unsigned int i = 0; i < levels.length (); ++i)
+ {
+ unsigned int total_insns = 0;
+ unsigned int level_cycles = 0;
+
+ for (unsigned int j = 0; j < NOCE_PARALLEL_NUM_CLASSES; ++j)
+ if (levels[i].insns[j] != 0)
+ {
+ total_insns += levels[i].insns[j];
+ level_cycles
+ = MAX (level_cycles,
+ CEIL (levels[i].insns[j], levels[i].widths[j]));
+ }
+
+ level_cycles = MAX (level_cycles, CEIL (total_insns, issue_rate));
+ parallel_cost += COSTS_N_INSNS (level_cycles);
+ }
+
+ return MIN (serial_cost, parallel_cost);
+}
+
/* Return true if SEQ is a good candidate as a replacement for the
if-convertible sequence described in IF_INFO.
This is the default implementation that targets can override
@@ -815,7 +1064,7 @@ default_noce_conversion_profitable_p (rtx_insn *seq,
bool speed_p = if_info->speed_p;
/* Cost up the new sequence. */
- unsigned int cost = seq_cost (seq, speed_p);
+ unsigned int cost = noce_parallel_seq_cost (seq, speed_p);
if (cost <= if_info->original_cost)
return true;
--
2.34.1