Try harder to preserve operand ties in maybe_legitimize_operands
authorRichard Sandiford <richard.sandiford@linaro.org>
Fri, 25 May 2018 06:35:30 +0000 (06:35 +0000)
committerRichard Sandiford <rsandifo@gcc.gnu.org>
Fri, 25 May 2018 06:35:30 +0000 (06:35 +0000)
maybe_legitimize_operands normally goes through each operand in turn
and legitimises it in isolation.  For example, if two operands to
an instruction initially have constant value C, and the instruction
requires both operands to be registers, the function ends up forcing
C into a register twice and passing two different registers to the
instruction.

I think we should try a bit harder to preserve the rtx_equal_p
property, if it's easy to do.  Some targets can optimise that
case better than they would the general case of all operands
being different.  This is particularly true for SVE after the
upcoming changes to the IFN_COND_* routines.

This is hard to test on its own, but is covered by the upcoming
IFN_COND_* patches.

2018-05-25  Richard Sandiford  <richard.sandiford@linaro.org>

gcc/
* optabs.c (can_reuse_operands_p): New function.
(maybe_legitimize_operands): Try to reuse the results for
earlier operands.

From-SVN: r260706

gcc/ChangeLog
gcc/optabs.c

index 0bba637a95a1a72ee8110faa239ec19a7952ed56..117844058476ae25e067154adf3ff6a0243f1448 100644 (file)
@@ -1,3 +1,9 @@
+2018-05-25  Richard Sandiford  <richard.sandiford@linaro.org>
+
+       * optabs.c (can_reuse_operands_p): New function.
+       (maybe_legitimize_operands): Try to reuse the results for
+       earlier operands.
+
 2018-05-24  Uros Bizjak  <ubizjak@gmail.com>
 
        * config/i386/sse.md (cvtusi2<ssescalarmodesuffix>64<round_name>):
index 53a147c8ef6b524629144bfceaef8253c3d1409b..278046aa06139e8a1aa5cad55cdad2258f6f3c4f 100644 (file)
@@ -7207,6 +7207,44 @@ create_convert_operand_from_type (struct expand_operand *op,
                               TYPE_UNSIGNED (type));
 }
 
+/* Return true if the requirements on operands OP1 and OP2 of instruction
+   ICODE are similar enough for the result of legitimizing OP1 to be
+   reusable for OP2.  OPNO1 and OPNO2 are the operand numbers associated
+   with OP1 and OP2 respectively.  */
+
+static inline bool
+can_reuse_operands_p (enum insn_code icode,
+                     unsigned int opno1, unsigned int opno2,
+                     const struct expand_operand *op1,
+                     const struct expand_operand *op2)
+{
+  /* Check requirements that are common to all types.  */
+  if (op1->type != op2->type
+      || op1->mode != op2->mode
+      || (insn_data[(int) icode].operand[opno1].mode
+         != insn_data[(int) icode].operand[opno2].mode))
+    return false;
+
+  /* Check the requirements for specific types.  */
+  switch (op1->type)
+    {
+    case EXPAND_OUTPUT:
+      /* Outputs must remain distinct.  */
+      return false;
+
+    case EXPAND_FIXED:
+    case EXPAND_INPUT:
+    case EXPAND_ADDRESS:
+    case EXPAND_INTEGER:
+      return true;
+
+    case EXPAND_CONVERT_TO:
+    case EXPAND_CONVERT_FROM:
+      return op1->unsigned_p == op2->unsigned_p;
+    }
+  gcc_unreachable ();
+}
+
 /* Try to make operands [OPS, OPS + NOPS) match operands [OPNO, OPNO + NOPS)
    of instruction ICODE.  Return true on success, leaving the new operand
    values in the OPS themselves.  Emit no code on failure.  */
@@ -7215,16 +7253,36 @@ bool
 maybe_legitimize_operands (enum insn_code icode, unsigned int opno,
                           unsigned int nops, struct expand_operand *ops)
 {
-  rtx_insn *last;
-  unsigned int i;
+  rtx_insn *last = get_last_insn ();
+  rtx *orig_values = XALLOCAVEC (rtx, nops);
+  for (unsigned int i = 0; i < nops; i++)
+    {
+      orig_values[i] = ops[i].value;
+
+      /* First try reusing the result of an earlier legitimization.
+        This avoids duplicate rtl and ensures that tied operands
+        remain tied.
+
+        This search is linear, but NOPS is bounded at compile time
+        to a small number (current a single digit).  */
+      unsigned int j = 0;
+      for (; j < i; ++j)
+       if (can_reuse_operands_p (icode, opno + j, opno + i, &ops[j], &ops[i])
+           && rtx_equal_p (orig_values[j], orig_values[i])
+           && ops[j].value
+           && insn_operand_matches (icode, opno + i, ops[j].value))
+         {
+           ops[i].value = copy_rtx (ops[j].value);
+           break;
+         }
 
-  last = get_last_insn ();
-  for (i = 0; i < nops; i++)
-    if (!maybe_legitimize_operand (icode, opno + i, &ops[i]))
-      {
-       delete_insns_since (last);
-       return false;
-      }
+      /* Otherwise try legitimizing the operand on its own.  */
+      if (j == i && !maybe_legitimize_operand (icode, opno + i, &ops[i]))
+       {
+         delete_insns_since (last);
+         return false;
+       }
+    }
   return true;
 }