re PR tree-optimization/79390 (10% performance drop in SciMark2 LU after r242550)
authorJakub Jelinek <jakub@redhat.com>
Tue, 4 Apr 2017 17:52:27 +0000 (19:52 +0200)
committerJakub Jelinek <jakub@gcc.gnu.org>
Tue, 4 Apr 2017 17:52:27 +0000 (19:52 +0200)
PR tree-optimization/79390
* target.h (struct noce_if_info): Declare.
* targhooks.h (default_noce_conversion_profitable_p): Declare.
* target.def (noce_conversion_profitable_p): New target hook.
* ifcvt.h (struct noce_if_info): New type, moved from ...
* ifcvt.c (struct noce_if_info): ... here.
(noce_conversion_profitable_p): Renamed to ...
(default_noce_conversion_profitable_p): ... this.  No longer
static nor inline.
(noce_try_store_flag_constants, noce_try_addcc,
noce_try_store_flag_mask, noce_try_cmove, noce_try_cmove_arith,
noce_convert_multiple_sets): Use targetm.noce_conversion_profitable_p
instead of noce_conversion_profitable_p.
* config/i386/i386.c: Include ifcvt.h.
(ix86_option_override_internal): Don't override
PARAM_MAX_RTL_IF_CONVERSION_INSNS default.
(ix86_noce_conversion_profitable_p): New function.
(TARGET_NOCE_CONVERSION_PROFITABLE_P): Redefine.
* config/i386/x86-tune.def (X86_TUNE_ONE_IF_CONV_INSN): Adjust comment.
* doc/tm.texi.in (TARGET_NOCE_CONVERSION_PROFITABLE_P): Add.
* doc/tm.texi: Regenerated.

* gcc.target/i386/pr79390.c: New test.
* gcc.dg/ifcvt-4.c: Use -mtune-ctrl=^one_if_conv_insn for i?86/x86_64.

From-SVN: r246686

13 files changed:
gcc/ChangeLog
gcc/config/i386/i386.c
gcc/config/i386/x86-tune.def
gcc/doc/tm.texi
gcc/doc/tm.texi.in
gcc/ifcvt.c
gcc/ifcvt.h
gcc/target.def
gcc/target.h
gcc/targhooks.h
gcc/testsuite/ChangeLog
gcc/testsuite/gcc.dg/ifcvt-4.c
gcc/testsuite/gcc.target/i386/pr79390.c [new file with mode: 0644]

index d60dd0b91e171fec37f67576bc15c2302fb9a1fe..d537ac5099a6366f372f6ed502b36a7379914668 100644 (file)
@@ -1,3 +1,27 @@
+2017-04-04  Jakub Jelinek  <jakub@redhat.com>
+
+       PR tree-optimization/79390
+       * target.h (struct noce_if_info): Declare.
+       * targhooks.h (default_noce_conversion_profitable_p): Declare.
+       * target.def (noce_conversion_profitable_p): New target hook.
+       * ifcvt.h (struct noce_if_info): New type, moved from ...
+       * ifcvt.c (struct noce_if_info): ... here.
+       (noce_conversion_profitable_p): Renamed to ...
+       (default_noce_conversion_profitable_p): ... this.  No longer
+       static nor inline.
+       (noce_try_store_flag_constants, noce_try_addcc,
+       noce_try_store_flag_mask, noce_try_cmove, noce_try_cmove_arith,
+       noce_convert_multiple_sets): Use targetm.noce_conversion_profitable_p
+       instead of noce_conversion_profitable_p.
+       * config/i386/i386.c: Include ifcvt.h.
+       (ix86_option_override_internal): Don't override
+       PARAM_MAX_RTL_IF_CONVERSION_INSNS default.
+       (ix86_noce_conversion_profitable_p): New function.
+       (TARGET_NOCE_CONVERSION_PROFITABLE_P): Redefine.
+       * config/i386/x86-tune.def (X86_TUNE_ONE_IF_CONV_INSN): Adjust comment.
+       * doc/tm.texi.in (TARGET_NOCE_CONVERSION_PROFITABLE_P): Add.
+       * doc/tm.texi: Regenerated.
+
 2017-04-04  Bill Schmidt  <wschmidt@linux.vnet.ibm.com>
 
        * doc/extend.texi (PowerPC AltiVec Built-in Functions): Grammar
index 118ab50b6bb29afdf9a220d0b78d6cb2b5d2ede4..96faffd81e876fb0b428f3a652c98ef40f47acbf 100644 (file)
@@ -84,6 +84,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "selftest-rtl.h"
 #include "print-rtl.h"
 #include "intl.h"
+#include "ifcvt.h"
 
 /* This file should be included last.  */
 #include "target-def.h"
@@ -6104,13 +6105,6 @@ ix86_option_override_internal (bool main_args_p,
                         opts->x_param_values,
                         opts_set->x_param_values);
 
-  /* Restrict number of if-converted SET insns to 1.  */
-  if (TARGET_ONE_IF_CONV_INSN)
-    maybe_set_param_value (PARAM_MAX_RTL_IF_CONVERSION_INSNS,
-                          1,
-                          opts->x_param_values,
-                          opts_set->x_param_values);
-
   /* Enable sw prefetching at -O3 for CPUS that prefetching is helpful.  */
   if (opts->x_flag_prefetch_loop_arrays < 0
       && HAVE_prefetch
@@ -50629,6 +50623,41 @@ ix86_max_noce_ifcvt_seq_cost (edge e)
     return BRANCH_COST (true, predictable_p) * COSTS_N_INSNS (2);
 }
 
+/* Return true if SEQ is a good candidate as a replacement for the
+   if-convertible sequence described in IF_INFO.  */
+
+static bool
+ix86_noce_conversion_profitable_p (rtx_insn *seq, struct noce_if_info *if_info)
+{
+  if (TARGET_ONE_IF_CONV_INSN && if_info->speed_p)
+    {
+      int cmov_cnt = 0;
+      /* Punt if SEQ contains more than one CMOV or FCMOV instruction.
+        Maybe we should allow even more conditional moves as long as they
+        are used far enough not to stall the CPU, or also consider
+        IF_INFO->TEST_BB succ edge probabilities.  */
+      for (rtx_insn *insn = seq; insn; insn = NEXT_INSN (insn))
+       {
+         rtx set = single_set (insn);
+         if (!set)
+           continue;
+         if (GET_CODE (SET_SRC (set)) != IF_THEN_ELSE)
+           continue;
+         rtx src = SET_SRC (set);
+         enum machine_mode mode = GET_MODE (src);
+         if (GET_MODE_CLASS (mode) != MODE_INT
+             && GET_MODE_CLASS (mode) != MODE_FLOAT)
+           continue;
+         if ((!REG_P (XEXP (src, 1)) && !MEM_P (XEXP (src, 1)))
+             || (!REG_P (XEXP (src, 2)) && !MEM_P (XEXP (src, 2))))
+           continue;
+         /* insn is CMOV or FCMOV.  */
+         if (++cmov_cnt > 1)
+           return false;
+       }
+    }
+  return default_noce_conversion_profitable_p (seq, if_info);
+}
 
 /* Implement targetm.vectorize.init_cost.  */
 
@@ -52181,6 +52210,10 @@ ix86_run_selftests (void)
 
 #undef TARGET_MAX_NOCE_IFCVT_SEQ_COST
 #define TARGET_MAX_NOCE_IFCVT_SEQ_COST ix86_max_noce_ifcvt_seq_cost
+
+#undef TARGET_NOCE_CONVERSION_PROFITABLE_P
+#define TARGET_NOCE_CONVERSION_PROFITABLE_P ix86_noce_conversion_profitable_p
+
 #if CHECKING_P
 #undef TARGET_RUN_TARGET_SELFTESTS
 #define TARGET_RUN_TARGET_SELFTESTS selftest::ix86_run_selftests
index 19b7daedfdd8de57d66d437c0011d3293668fdda..c642f452e00f613ee37f25595d455939cddfa098 100644 (file)
@@ -547,7 +547,7 @@ DEF_TUNE (X86_TUNE_PROMOTE_QI_REGS, "promote_qi_regs", 0U)
    the unroll factor so that the unrolled loop fits the loop buffer.  */
 DEF_TUNE (X86_TUNE_ADJUST_UNROLL, "adjust_unroll_factor", m_BDVER3 | m_BDVER4)
 
-/* X86_TUNE_ONE_IF_CONV_INSNS: Restrict a number of set insns to be
-   if-converted to one.  */
+/* X86_TUNE_ONE_IF_CONV_INSNS: Restrict a number of cmov insns in
+   if-converted sequence to one.  */
 DEF_TUNE (X86_TUNE_ONE_IF_CONV_INSN, "one_if_conv_insn",
          m_SILVERMONT | m_KNL | m_INTEL | m_CORE_ALL | m_GENERIC)
index d59a4b90c54709fdd11be20d1bc83b202fbeac69..c4f2c893c8e63b7c9df6626ca3a0710c6aad6933 100644 (file)
@@ -6644,6 +6644,12 @@ The default implementation of this hook uses the
 and uses a multiple of @code{BRANCH_COST} otherwise.
 @end deftypefn
 
+@deftypefn {Target Hook} bool TARGET_NOCE_CONVERSION_PROFITABLE_P (rtx_insn *@var{seq}, struct noce_if_info *@var{if_info})
+This hook returns true if the instruction sequence @code{seq} is a good
+candidate as a replacement for the if-convertible sequence described in
+@code{if_info}.
+@end deftypefn
+
 @deftypefn {Target Hook} bool TARGET_NO_SPECULATION_IN_DELAY_SLOTS_P (void)
 This predicate controls the use of the eager delay slot filler to disallow
 speculatively executed instructions being placed in delay slots.  Targets
index 88c12dddb2515bcb359f49df470e494d111d28bd..1c471d8da35f3640d9a34f4a760b4e25969a7ad0 100644 (file)
@@ -4796,6 +4796,8 @@ Define this macro if a non-short-circuit operation produced by
 
 @hook TARGET_MAX_NOCE_IFCVT_SEQ_COST
 
+@hook TARGET_NOCE_CONVERSION_PROFITABLE_P
+
 @hook TARGET_NO_SPECULATION_IN_DELAY_SLOTS_P
 
 @node Scheduling
index 4fd1744643ec1b5ccc1e944e3a1ffc0d90aa9138..e51ccab26b2984f8831f9af49ff77e53e05ec917 100644 (file)
@@ -760,76 +760,6 @@ cond_exec_process_if_block (ce_if_block * ce_info,
   return FALSE;
 }
 \f
-/* Used by noce_process_if_block to communicate with its subroutines.
-
-   The subroutines know that A and B may be evaluated freely.  They
-   know that X is a register.  They should insert new instructions
-   before cond_earliest.  */
-
-struct noce_if_info
-{
-  /* The basic blocks that make up the IF-THEN-{ELSE-,}JOIN block.  */
-  basic_block test_bb, then_bb, else_bb, join_bb;
-
-  /* The jump that ends TEST_BB.  */
-  rtx_insn *jump;
-
-  /* The jump condition.  */
-  rtx cond;
-
-  /* Reversed jump condition.  */
-  rtx rev_cond;
-
-  /* New insns should be inserted before this one.  */
-  rtx_insn *cond_earliest;
-
-  /* Insns in the THEN and ELSE block.  There is always just this
-     one insns in those blocks.  The insns are single_set insns.
-     If there was no ELSE block, INSN_B is the last insn before
-     COND_EARLIEST, or NULL_RTX.  In the former case, the insn
-     operands are still valid, as if INSN_B was moved down below
-     the jump.  */
-  rtx_insn *insn_a, *insn_b;
-
-  /* The SET_SRC of INSN_A and INSN_B.  */
-  rtx a, b;
-
-  /* The SET_DEST of INSN_A.  */
-  rtx x;
-
-  /* The original set destination that the THEN and ELSE basic blocks finally
-     write their result to.  */
-  rtx orig_x;
-  /* True if this if block is not canonical.  In the canonical form of
-     if blocks, the THEN_BB is the block reached via the fallthru edge
-     from TEST_BB.  For the noce transformations, we allow the symmetric
-     form as well.  */
-  bool then_else_reversed;
-
-  /* True if the contents of then_bb and else_bb are a
-     simple single set instruction.  */
-  bool then_simple;
-  bool else_simple;
-
-  /* True if we're optimisizing the control block for speed, false if
-     we're optimizing for size.  */
-  bool speed_p;
-
-  /* An estimate of the original costs.  When optimizing for size, this is the
-     combined cost of COND, JUMP and the costs for THEN_BB and ELSE_BB.
-     When optimizing for speed, we use the costs of COND plus the minimum of
-     the costs for THEN_BB and ELSE_BB, as computed in the next field.  */
-  unsigned int original_cost;
-
-  /* Maximum permissible cost for the unconditional sequence we should
-     generate to replace this branch.  */
-  unsigned int max_seq_cost;
-
-  /* The name of the noce transform that succeeded in if-converting
-     this structure.  Used for debugging.  */
-  const char *transform_name;
-};
-
 static rtx noce_emit_store_flag (struct noce_if_info *, rtx, int, int);
 static int noce_try_move (struct noce_if_info *);
 static int noce_try_ifelse_collapse (struct noce_if_info *);
@@ -857,11 +787,14 @@ noce_reversed_cond_code (struct noce_if_info *if_info)
   return reversed_comparison_code (if_info->cond, if_info->jump);
 }
 
-/* Return TRUE if SEQ is a good candidate as a replacement for the
-   if-convertible sequence described in IF_INFO.  */
+/* 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
+   through a target hook.  */
 
-inline static bool
-noce_conversion_profitable_p (rtx_insn *seq, struct noce_if_info *if_info)
+bool
+default_noce_conversion_profitable_p (rtx_insn *seq,
+                                     struct noce_if_info *if_info)
 {
   bool speed_p = if_info->speed_p;
 
@@ -1544,7 +1477,7 @@ noce_try_store_flag_constants (struct noce_if_info *if_info)
        noce_emit_move_insn (if_info->x, target);
 
       seq = end_ifcvt_sequence (if_info);
-      if (!seq || !noce_conversion_profitable_p (seq, if_info))
+      if (!seq || !targetm.noce_conversion_profitable_p (seq, if_info))
        return FALSE;
 
       emit_insn_before_setloc (seq, if_info->jump,
@@ -1605,7 +1538,7 @@ noce_try_addcc (struct noce_if_info *if_info)
                noce_emit_move_insn (if_info->x, target);
 
              seq = end_ifcvt_sequence (if_info);
-             if (!seq || !noce_conversion_profitable_p (seq, if_info))
+             if (!seq || !targetm.noce_conversion_profitable_p (seq, if_info))
                return FALSE;
 
              emit_insn_before_setloc (seq, if_info->jump,
@@ -1647,7 +1580,7 @@ noce_try_addcc (struct noce_if_info *if_info)
                noce_emit_move_insn (if_info->x, target);
 
              seq = end_ifcvt_sequence (if_info);
-             if (!seq || !noce_conversion_profitable_p (seq, if_info))
+             if (!seq || !targetm.noce_conversion_profitable_p (seq, if_info))
                return FALSE;
 
              emit_insn_before_setloc (seq, if_info->jump,
@@ -1698,7 +1631,7 @@ noce_try_store_flag_mask (struct noce_if_info *if_info)
            noce_emit_move_insn (if_info->x, target);
 
          seq = end_ifcvt_sequence (if_info);
-         if (!seq || !noce_conversion_profitable_p (seq, if_info))
+         if (!seq || !targetm.noce_conversion_profitable_p (seq, if_info))
            return FALSE;
 
          emit_insn_before_setloc (seq, if_info->jump,
@@ -1850,7 +1783,7 @@ noce_try_cmove (struct noce_if_info *if_info)
            noce_emit_move_insn (if_info->x, target);
 
          seq = end_ifcvt_sequence (if_info);
-         if (!seq || !noce_conversion_profitable_p (seq, if_info))
+         if (!seq || !targetm.noce_conversion_profitable_p (seq, if_info))
            return FALSE;
 
          emit_insn_before_setloc (seq, if_info->jump,
@@ -1903,7 +1836,7 @@ noce_try_cmove (struct noce_if_info *if_info)
                noce_emit_move_insn (if_info->x, target);
 
              seq = end_ifcvt_sequence (if_info);
-             if (!seq || !noce_conversion_profitable_p (seq, if_info))
+             if (!seq || !targetm.noce_conversion_profitable_p (seq, if_info))
                return FALSE;
 
              emit_insn_before_setloc (seq, if_info->jump,
@@ -2345,7 +2278,7 @@ noce_try_cmove_arith (struct noce_if_info *if_info)
     noce_emit_move_insn (x, target);
 
   ifcvt_seq = end_ifcvt_sequence (if_info);
-  if (!ifcvt_seq || !noce_conversion_profitable_p (ifcvt_seq, if_info))
+  if (!ifcvt_seq || !targetm.noce_conversion_profitable_p (ifcvt_seq, if_info))
     return FALSE;
 
   emit_insn_before_setloc (ifcvt_seq, if_info->jump,
@@ -3308,7 +3241,7 @@ noce_convert_multiple_sets (struct noce_if_info *if_info)
   /* Actually emit the sequence if it isn't too expensive.  */
   rtx_insn *seq = get_insns ();
 
-  if (!noce_conversion_profitable_p (seq, if_info))
+  if (!targetm.noce_conversion_profitable_p (seq, if_info))
     {
       end_sequence ();
       return FALSE;
index e6276bb6a1ed208d5c2c9e8d037ed825776f5630..1935de5e82f76a72b5b678b2abf4a7cc88d4abbc 100644 (file)
@@ -40,4 +40,74 @@ struct ce_if_block
   int pass;                            /* Pass number.  */
 };
 
+/* Used by noce_process_if_block to communicate with its subroutines.
+
+   The subroutines know that A and B may be evaluated freely.  They
+   know that X is a register.  They should insert new instructions
+   before cond_earliest.  */
+
+struct noce_if_info
+{
+  /* The basic blocks that make up the IF-THEN-{ELSE-,}JOIN block.  */
+  basic_block test_bb, then_bb, else_bb, join_bb;
+
+  /* The jump that ends TEST_BB.  */
+  rtx_insn *jump;
+
+  /* The jump condition.  */
+  rtx cond;
+
+  /* Reversed jump condition.  */
+  rtx rev_cond;
+
+  /* New insns should be inserted before this one.  */
+  rtx_insn *cond_earliest;
+
+  /* Insns in the THEN and ELSE block.  There is always just this
+     one insns in those blocks.  The insns are single_set insns.
+     If there was no ELSE block, INSN_B is the last insn before
+     COND_EARLIEST, or NULL_RTX.  In the former case, the insn
+     operands are still valid, as if INSN_B was moved down below
+     the jump.  */
+  rtx_insn *insn_a, *insn_b;
+
+  /* The SET_SRC of INSN_A and INSN_B.  */
+  rtx a, b;
+
+  /* The SET_DEST of INSN_A.  */
+  rtx x;
+
+  /* The original set destination that the THEN and ELSE basic blocks finally
+     write their result to.  */
+  rtx orig_x;
+  /* True if this if block is not canonical.  In the canonical form of
+     if blocks, the THEN_BB is the block reached via the fallthru edge
+     from TEST_BB.  For the noce transformations, we allow the symmetric
+     form as well.  */
+  bool then_else_reversed;
+
+  /* True if the contents of then_bb and else_bb are a
+     simple single set instruction.  */
+  bool then_simple;
+  bool else_simple;
+
+  /* True if we're optimisizing the control block for speed, false if
+     we're optimizing for size.  */
+  bool speed_p;
+
+  /* An estimate of the original costs.  When optimizing for size, this is the
+     combined cost of COND, JUMP and the costs for THEN_BB and ELSE_BB.
+     When optimizing for speed, we use the costs of COND plus the minimum of
+     the costs for THEN_BB and ELSE_BB, as computed in the next field.  */
+  unsigned int original_cost;
+
+  /* Maximum permissible cost for the unconditional sequence we should
+     generate to replace this branch.  */
+  unsigned int max_seq_cost;
+
+  /* The name of the noce transform that succeeded in if-converting
+     this structure.  Used for debugging.  */
+  const char *transform_name;
+};
+
 #endif /* GCC_IFCVT_H */
index 43600aecd3f30b0819c2dd6cd4b5fa282528405a..6bebfd5b9d6b7001a19fd5a41700ac3667408c00 100644 (file)
@@ -3660,6 +3660,16 @@ and uses a multiple of @code{BRANCH_COST} otherwise.",
 unsigned int, (edge e),
 default_max_noce_ifcvt_seq_cost)
 
+/* Return true if the given instruction sequence is a good candidate
+   as a replacement for the if-convertible sequence.  */
+DEFHOOK
+(noce_conversion_profitable_p,
+ "This hook returns true if the instruction sequence @code{seq} is a good\n\
+candidate as a replacement for the if-convertible sequence described in\n\
+@code{if_info}.",
+bool, (rtx_insn *seq, struct noce_if_info *if_info),
+default_noce_conversion_profitable_p)
+
 /* Permit speculative instructions in delay slots during delayed-branch 
    scheduling.  */
 DEFHOOK
index 63390dba27f0beb226f84e22f25afa92d472db0a..393de408b9726b2242303081d28ebe49c2325998 100644 (file)
@@ -140,6 +140,9 @@ struct ddg;
 /* This is defined in cfgloop.h .  */
 struct loop;
 
+/* This is defined in ifcvt.h.  */
+struct noce_if_info;
+
 /* This is defined in tree-ssa-alias.h.  */
 struct ao_ref;
 
index a5565f5bf53737cec57f04991c274f2377b1f336..18070df7839f5515e55a841c91bb0d95f86b1099 100644 (file)
@@ -257,6 +257,8 @@ extern void default_setup_incoming_vararg_bounds (cumulative_args_t ca ATTRIBUTE
 extern bool default_optab_supported_p (int, machine_mode, machine_mode,
                                       optimization_type);
 extern unsigned int default_max_noce_ifcvt_seq_cost (edge);
+extern bool default_noce_conversion_profitable_p (rtx_insn *,
+                                                 struct noce_if_info *);
 extern unsigned int default_min_arithmetic_precision (void);
 
 extern enum flt_eval_method
index 9c1a764a7eff1fdfbd7419609096468ba4ee383c..6d1ed2227b750c6774731e6aabfe7365d6bd0f2b 100644 (file)
@@ -1,3 +1,9 @@
+2017-04-04  Jakub Jelinek  <jakub@redhat.com>
+
+       PR tree-optimization/79390
+       * gcc.target/i386/pr79390.c: New test.
+       * gcc.dg/ifcvt-4.c: Use -mtune-ctrl=^one_if_conv_insn for i?86/x86_64.
+
 2017-04-04  Volker Reichelt  <v.reichelt@netcologne.de>
 
        PR c++/80296
index b4a4bc8b62f1db78be05f6c69f6314fd94c520e7..87bae3cbe440f942fd2f1a9d7d987c333ec09740 100644 (file)
@@ -1,6 +1,7 @@
 /* { dg-options "-fdump-rtl-ce1 -O2 --param max-rtl-if-conversion-insns=3 --param max-rtl-if-conversion-unpredictable-cost=100" } */
 /* { dg-additional-options "-misel" { target { powerpc*-*-* } } } */
 /* { dg-additional-options "-march=z196" { target { s390x-*-* } } } */
+/* { dg-additional-options "-mtune-ctrl=^one_if_conv_insn" { target { i?86-*-* x86_64-*-* } } } */
 /* { dg-skip-if "Multiple set if-conversion not guaranteed on all subtargets" { "arm*-*-* hppa*64*-*-* s390-*-* visium-*-*" riscv*-*-* } }  */
 /* { dg-skip-if "" { "s390x-*-*" } { "-m31" } }  */
 
diff --git a/gcc/testsuite/gcc.target/i386/pr79390.c b/gcc/testsuite/gcc.target/i386/pr79390.c
new file mode 100644 (file)
index 0000000..1c7d601
--- /dev/null
@@ -0,0 +1,28 @@
+/* PR tree-optimization/79390 */
+/* { dg-do compile { target lp64 } } */
+/* { dg-options "-O2 -march=haswell -mtune=haswell -mfpmath=sse" } */
+/* Make sure we emit a conditional move in this loop.  */
+
+extern double A[32];
+
+int
+foo (void)
+{
+  double t = A[0];
+  int jp = 0;
+  int i;
+
+  for (i = 0; i < 32; i++)
+    {
+      double ab = A[i];
+      if (ab > t)
+       {
+         jp = i;
+         t = ab;
+       }
+    }
+  return jp;
+}
+
+/* { dg-final { scan-assembler "\[ \\t\]cmov\[a-z]+\[ \\t\]" } } */