From f647f862ae3cf4f8390f6f876b6e25ec775c81ae Mon Sep 17 00:00:00 2001
From: Kaipeng Zhou <zhoukaipeng3@huawei.com>
Date: Mon, 20 Jul 2020 23:00:21 +0800
Subject: [PATCH] Add #pragma GCC no_reduc_chain.

This macro definition can control the loop vectorization skipping
to find sequences from reduction chains.

gcc/c-family/ChangeLog:

	* c-pragma.c (init_pragma): Register pragma GCC no_reduc_chain.
	* c-pragma.h (enum pragma_kind): Add PRAGMA_NO_REDUC_CHAIN.

gcc/c/ChangeLog:

	* c-parser.c (c_parser_while_statement): Add no_reduc_chain parameter
	and build ANNOTATE_EXPR if present.  Add 4th operand to ANNOTATE_EXPR.
	(c_parser_do_statement): Likewise.
	(c_parser_for_statement): Likewise.
	(c_parser_statement_after_labels): Adjust calls to above.
	(c_parser_pragma_no_reduc_chain): New static function.
	(c_parser_pragma): Add support for pragma no_reduc_chain.

gcc/ChangeLog:

	* cfgloop.h: A field value is added to the loop structure to indicate
	whether to skip finding sequences from complex chains.
	* gimplify.c (gimple_boolify): Deal with no_reduc_chain kind.
	* tree-cfg.c (replace_loop_annotate_in_block): New case for
	annot_expr_unroll_kind.
	* tree-core.h (enum annot_expr_kind): Add annot_expr_unroll_kind.
	* tree-vect-slp.c (vect_analyze_slp): If pragma GCC no_reduc_chain is
	set true, skip finding sequences from reduction chains.

gcc/testsuite/ChangeLog:

	* gcc.dg/vect/pr96053.c: New test.
---
 gcc/c-family/c-pragma.c             |   4 +
 gcc/c-family/c-pragma.h             |   1 +
 gcc/c/c-parser.c                    | 145 +++++++++++++++++++---------
 gcc/cfgloop.h                       |   4 +
 gcc/gimplify.c                      |   1 +
 gcc/testsuite/gcc.dg/vect/pr96053.c |  18 ++++
 gcc/tree-cfg.c                      |   3 +
 gcc/tree-core.h                     |   1 +
 gcc/tree-vect-slp.c                 |   6 +-
 9 files changed, 135 insertions(+), 48 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/vect/pr96053.c

diff --git a/gcc/c-family/c-pragma.c b/gcc/c-family/c-pragma.c
index e3169e68fb6..f3cc1687678 100644
--- a/gcc/c-family/c-pragma.c
+++ b/gcc/c-family/c-pragma.c
@@ -1560,6 +1560,10 @@ init_pragma (void)
     cpp_register_deferred_pragma (parse_in, "GCC", "unroll", PRAGMA_UNROLL,
 				  false, false);
 
+  if (!flag_preprocess_only)
+    cpp_register_deferred_pragma (parse_in, "GCC", "no_reduc_chain",
+				  PRAGMA_NO_REDUC_CHAIN, false, false);
+
 #ifdef HANDLE_PRAGMA_PACK_WITH_EXPANSION
   c_register_pragma_with_expansion (0, "pack", handle_pragma_pack);
 #else
diff --git a/gcc/c-family/c-pragma.h b/gcc/c-family/c-pragma.h
index 3e9695fae86..865978eb2f4 100644
--- a/gcc/c-family/c-pragma.h
+++ b/gcc/c-family/c-pragma.h
@@ -75,6 +75,7 @@ enum pragma_kind {
   PRAGMA_GCC_PCH_PREPROCESS,
   PRAGMA_IVDEP,
   PRAGMA_UNROLL,
+  PRAGMA_NO_REDUC_CHAIN,
 
   PRAGMA_FIRST_EXTERNAL
 };
diff --git a/gcc/c/c-parser.c b/gcc/c/c-parser.c
index 7bf91ef91aa..06d06f29574 100644
--- a/gcc/c/c-parser.c
+++ b/gcc/c/c-parser.c
@@ -1525,9 +1525,11 @@ static tree c_parser_c99_block_statement (c_parser *, bool *,
 					  location_t * = NULL);
 static void c_parser_if_statement (c_parser *, bool *, vec<tree> *);
 static void c_parser_switch_statement (c_parser *, bool *);
-static void c_parser_while_statement (c_parser *, bool, unsigned short, bool *);
-static void c_parser_do_statement (c_parser *, bool, unsigned short);
-static void c_parser_for_statement (c_parser *, bool, unsigned short, bool *);
+static void c_parser_while_statement (c_parser *, bool, unsigned short, bool,
+				      bool *);
+static void c_parser_do_statement (c_parser *, bool, unsigned short, bool);
+static void c_parser_for_statement (c_parser *, bool, unsigned short, bool,
+				    bool *);
 static tree c_parser_asm_statement (c_parser *);
 static tree c_parser_asm_operands (c_parser *);
 static tree c_parser_asm_goto_operands (c_parser *);
@@ -6191,13 +6193,13 @@ c_parser_statement_after_labels (c_parser *parser, bool *if_p,
 	  c_parser_switch_statement (parser, if_p);
 	  break;
 	case RID_WHILE:
-	  c_parser_while_statement (parser, false, 0, if_p);
+	  c_parser_while_statement (parser, false, 0, false, if_p);
 	  break;
 	case RID_DO:
-	  c_parser_do_statement (parser, 0, false);
+	  c_parser_do_statement (parser, 0, false, false);
 	  break;
 	case RID_FOR:
-	  c_parser_for_statement (parser, false, 0, if_p);
+	  c_parser_for_statement (parser, false, 0, false, if_p);
 	  break;
 	case RID_GOTO:
 	  c_parser_consume_token (parser);
@@ -6688,7 +6690,7 @@ c_parser_switch_statement (c_parser *parser, bool *if_p)
 
 static void
 c_parser_while_statement (c_parser *parser, bool ivdep, unsigned short unroll,
-			  bool *if_p)
+			  bool no_reduc_chain, bool *if_p)
 {
   tree block, cond, body, save_break, save_cont;
   location_t loc;
@@ -6709,6 +6711,11 @@ c_parser_while_statement (c_parser *parser, bool ivdep, unsigned short unroll,
 		   build_int_cst (integer_type_node,
 				  annot_expr_unroll_kind),
 		   build_int_cst (integer_type_node, unroll));
+  if (no_reduc_chain && cond != error_mark_node)
+    cond = build3 (ANNOTATE_EXPR, TREE_TYPE (cond), cond,
+		   build_int_cst (integer_type_node,
+				  annot_expr_no_reduc_chain_kind),
+		   integer_zero_node);
   save_break = c_break_label;
   c_break_label = NULL_TREE;
   save_cont = c_cont_label;
@@ -6744,7 +6751,8 @@ c_parser_while_statement (c_parser *parser, bool ivdep, unsigned short unroll,
 */
 
 static void
-c_parser_do_statement (c_parser *parser, bool ivdep, unsigned short unroll)
+c_parser_do_statement (c_parser *parser, bool ivdep, unsigned short unroll,
+		       bool no_reduc_chain)
 {
   tree block, cond, body, save_break, save_cont, new_break, new_cont;
   location_t loc;
@@ -6778,6 +6786,11 @@ c_parser_do_statement (c_parser *parser, bool ivdep, unsigned short unroll)
 		   build_int_cst (integer_type_node,
 				  annot_expr_unroll_kind),
  		   build_int_cst (integer_type_node, unroll));
+  if (no_reduc_chain && cond != error_mark_node)
+    cond = build3 (ANNOTATE_EXPR, TREE_TYPE (cond), cond,
+		   build_int_cst (integer_type_node,
+				  annot_expr_no_reduc_chain_kind),
+		   integer_zero_node);
   if (!c_parser_require (parser, CPP_SEMICOLON, "expected %<;%>"))
     c_parser_skip_to_end_of_block_or_statement (parser);
   c_finish_loop (loc, cond_loc, cond, UNKNOWN_LOCATION, NULL, body,
@@ -6846,7 +6859,7 @@ c_parser_do_statement (c_parser *parser, bool ivdep, unsigned short unroll)
 
 static void
 c_parser_for_statement (c_parser *parser, bool ivdep, unsigned short unroll,
-			bool *if_p)
+			bool no_reduc_chain, bool *if_p)
 {
   tree block, cond, incr, save_break, save_cont, body;
   /* The following are only used when parsing an ObjC foreach statement.  */
@@ -6981,6 +6994,12 @@ c_parser_for_statement (c_parser *parser, bool ivdep, unsigned short unroll,
 					  "with %<GCC unroll%> pragma");
 		  cond = error_mark_node;
 		}
+	      else if (no_reduc_chain)
+		{
+		  c_parser_error (parser, "missing loop condition in loop "
+					  "with %<GCC no_reduc_chain> pragma");
+		  cond = error_mark_node;
+		}
 	      else
 		{
 		  c_parser_consume_token (parser);
@@ -7003,6 +7022,11 @@ c_parser_for_statement (c_parser *parser, bool ivdep, unsigned short unroll,
  			   build_int_cst (integer_type_node,
 					  annot_expr_unroll_kind),
 			   build_int_cst (integer_type_node, unroll));
+	  if (no_reduc_chain && cond != error_mark_node)
+	    cond = build3 (ANNOTATE_EXPR, TREE_TYPE (cond), cond,
+			   build_int_cst (integer_type_node,
+					  annot_expr_no_reduc_chain_kind),
+			   integer_zero_node);
 	}
       /* Parse the increment expression (the third expression in a
 	 for-statement).  In the case of a foreach-statement, this is
@@ -12295,6 +12319,16 @@ c_parser_pragma_unroll (c_parser *parser)
   return unroll;
 }
 
+/* Parse a pragma GCC no_reduc_chain.  */
+
+static bool
+c_parser_pragma_no_reduc_chain (c_parser *parser)
+{
+  c_parser_consume_pragma (parser);
+  c_parser_skip_to_pragma_eol (parser);
+  return true;
+}
+
 /* Handle pragmas.  Some OpenMP pragmas are associated with, and therefore
    should be considered, statements.  ALLOW_STMT is true if we're within
    the context of a function and such pragmas are to be allowed.  Returns
@@ -12464,50 +12498,69 @@ c_parser_pragma (c_parser *parser, enum pragma_context context, bool *if_p)
       return c_parser_omp_ordered (parser, context, if_p);
 
     case PRAGMA_IVDEP:
-      {
-	const bool ivdep = c_parse_pragma_ivdep (parser);
-	unsigned short unroll;
-	if (c_parser_peek_token (parser)->pragma_kind == PRAGMA_UNROLL)
-	  unroll = c_parser_pragma_unroll (parser);
-	else
-	  unroll = 0;
-	if (!c_parser_next_token_is_keyword (parser, RID_FOR)
-	    && !c_parser_next_token_is_keyword (parser, RID_WHILE)
-	    && !c_parser_next_token_is_keyword (parser, RID_DO))
-	  {
-	    c_parser_error (parser, "for, while or do statement expected");
-	    return false;
-	  }
-	if (c_parser_next_token_is_keyword (parser, RID_FOR))
-	  c_parser_for_statement (parser, ivdep, unroll, if_p);
-	else if (c_parser_next_token_is_keyword (parser, RID_WHILE))
-	  c_parser_while_statement (parser, ivdep, unroll, if_p);
-	else
-	  c_parser_do_statement (parser, ivdep, unroll);
-      }
-      return false;
-
     case PRAGMA_UNROLL:
+    case PRAGMA_NO_REDUC_CHAIN:
       {
-	unsigned short unroll = c_parser_pragma_unroll (parser);
-	bool ivdep;
-	if (c_parser_peek_token (parser)->pragma_kind == PRAGMA_IVDEP)
-	  ivdep = c_parse_pragma_ivdep (parser);
-	else
-	  ivdep = false;
-	if (!c_parser_next_token_is_keyword (parser, RID_FOR)
-	    && !c_parser_next_token_is_keyword (parser, RID_WHILE)
-	    && !c_parser_next_token_is_keyword (parser, RID_DO))
+	bool ivdep = false;
+	unsigned short unroll = 0;
+	bool no_reduc_chain = false;
+
+	/* Signal variables used to ensure that macro definitions are not
+	   repeated.  */
+	unsigned short sign = 0;
+	do
 	  {
-	    c_parser_error (parser, "for, while or do statement expected");
-	    return false;
+	    if (id == PRAGMA_IVDEP)
+	      {
+		if (sign & 1)
+		  {
+		    c_parser_error (parser,
+				    "repeated pragma GCC ivdep statement");
+		    return false;
+		  }
+		ivdep = c_parse_pragma_ivdep (parser);
+		sign |= 1;
+	      }
+	    else if (id == PRAGMA_UNROLL)
+	      {
+		if (sign & 2)
+		  {
+		    c_parser_error (parser,
+				    "repeated pragma GCC unroll statement");
+		    return false;
+		  }
+		unroll = c_parser_pragma_unroll (parser);
+		sign |= 2;
+	      }
+	    else if (id == PRAGMA_NO_REDUC_CHAIN)
+	      {
+		if (sign & 4)
+		  {
+		    c_parser_error (parser,
+			    "repeated pragma GCC no_reduc_chain statement");
+		    return false;
+		  }
+		no_reduc_chain = c_parser_pragma_no_reduc_chain (parser);
+		sign |= 4;
+	      }
+	    else
+	      {
+		c_parser_error (parser, "for, while or do statement expected");
+		return false;
+	      }
+	    id = c_parser_peek_token (parser)->pragma_kind;
 	  }
+	while (!c_parser_next_token_is_keyword (parser, RID_FOR)
+	       && !c_parser_next_token_is_keyword (parser, RID_WHILE)
+	       && !c_parser_next_token_is_keyword (parser, RID_DO));
+
 	if (c_parser_next_token_is_keyword (parser, RID_FOR))
-	  c_parser_for_statement (parser, ivdep, unroll, if_p);
+	  c_parser_for_statement (parser, ivdep, unroll, no_reduc_chain, if_p);
 	else if (c_parser_next_token_is_keyword (parser, RID_WHILE))
-	  c_parser_while_statement (parser, ivdep, unroll, if_p);
+	  c_parser_while_statement (parser, ivdep, unroll, no_reduc_chain,
+				    if_p);
 	else
-	  c_parser_do_statement (parser, ivdep, unroll);
+	  c_parser_do_statement (parser, ivdep, unroll, no_reduc_chain);
       }
       return false;
 
diff --git a/gcc/cfgloop.h b/gcc/cfgloop.h
index 18b404e292f..4b1612151b7 100644
--- a/gcc/cfgloop.h
+++ b/gcc/cfgloop.h
@@ -230,6 +230,10 @@ public:
      flag_finite_loops or similar pragmas state.  */
   unsigned finite_p : 1;
 
+  /* True if the loop should never be slped by finding sequences from
+     reduction chains.  */
+  unsigned no_reduc_chain : 1;
+
   /* The number of times to unroll the loop.  0 means no information given,
      just do what we always do.  A value of 1 means do not unroll the loop.
      A value of USHRT_MAX means unroll with no specific unrolling factor.
diff --git a/gcc/gimplify.c b/gcc/gimplify.c
index 09a30cf69a5..bbb9443e7b0 100644
--- a/gcc/gimplify.c
+++ b/gcc/gimplify.c
@@ -3956,6 +3956,7 @@ gimple_boolify (tree expr)
 	{
 	case annot_expr_ivdep_kind:
 	case annot_expr_unroll_kind:
+	case annot_expr_no_reduc_chain_kind:
 	case annot_expr_no_vector_kind:
 	case annot_expr_vector_kind:
 	case annot_expr_parallel_kind:
diff --git a/gcc/testsuite/gcc.dg/vect/pr96053.c b/gcc/testsuite/gcc.dg/vect/pr96053.c
new file mode 100644
index 00000000000..4268cdfdbaa
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/vect/pr96053.c
@@ -0,0 +1,18 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -funsafe-math-optimizations -ftree-vectorize -fno-tree-reassoc -fdump-tree-vect-details -march=armv8.2-a+simd" } */
+
+double f(double *a, double *b)
+{
+  double res1 = 0;
+  double res0 = 0;
+#pragma GCC no_reduc_chain
+  for (int i = 0 ; i < 1000; i+=4) {
+    res0 += a[i] * b[i];
+    res1 += a[i+1] * b[i*1];
+    res0 += a[i+2] * b[i+2];
+    res1 += a[i+3] * b[i+3];
+  }
+  return res0 + res1;
+}
+
+/* { dg-final { scan-tree-dump-times "vectorized 1 loops" 1 "vect" } } */
diff --git a/gcc/tree-cfg.c b/gcc/tree-cfg.c
index b4d0c6db238..570d869c966 100644
--- a/gcc/tree-cfg.c
+++ b/gcc/tree-cfg.c
@@ -283,6 +283,9 @@ replace_loop_annotate_in_block (basic_block bb, class loop *loop)
 	    = (unsigned short) tree_to_shwi (gimple_call_arg (stmt, 2));
 	  cfun->has_unroll = true;
 	  break;
+	case annot_expr_no_reduc_chain_kind:
+	  loop->no_reduc_chain = true;
+	  break;
 	case annot_expr_no_vector_kind:
 	  loop->dont_vectorize = true;
 	  break;
diff --git a/gcc/tree-core.h b/gcc/tree-core.h
index 8c5a2e3c404..1207db34d8e 100644
--- a/gcc/tree-core.h
+++ b/gcc/tree-core.h
@@ -911,6 +911,7 @@ enum tree_node_kind {
 enum annot_expr_kind {
   annot_expr_ivdep_kind,
   annot_expr_unroll_kind,
+  annot_expr_no_reduc_chain_kind,
   annot_expr_no_vector_kind,
   annot_expr_vector_kind,
   annot_expr_parallel_kind,
diff --git a/gcc/tree-vect-slp.c b/gcc/tree-vect-slp.c
index 33fc87a9f86..d57c4f13016 100644
--- a/gcc/tree-vect-slp.c
+++ b/gcc/tree-vect-slp.c
@@ -2418,10 +2418,12 @@ vect_analyze_slp (vec_info *vinfo, unsigned max_tree_size)
     {
       if (loop_vinfo->reduction_chains.length () > 0)
 	{
+	  bool no_reduc_chain = LOOP_VINFO_LOOP (loop_vinfo)->no_reduc_chain;
 	  /* Find SLP sequences starting from reduction chains.  */
 	  FOR_EACH_VEC_ELT (loop_vinfo->reduction_chains, i, first_element)
-	    if (! vect_analyze_slp_instance (vinfo, bst_map, first_element,
-					     max_tree_size))
+	    if (no_reduc_chain
+		|| !vect_analyze_slp_instance (vinfo, bst_map, first_element,
+						max_tree_size))
 	      {
 		/* Dissolve reduction chain group.  */
 		stmt_vec_info vinfo = first_element;
-- 
2.19.1

