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

Reply via email to