c++: #pragma GCC unroll C++ fixes [PR112795]

Message ID ZWsMN6iX/Cp+B5qJ@tucnak
State Unresolved
Headers
Series c++: #pragma GCC unroll C++ fixes [PR112795] |

Checks

Context Check Description
snail/gcc-patch-check warning Git am fail log

Commit Message

Jakub Jelinek Dec. 2, 2023, 10:51 a.m. UTC
  Hi!

foo in the unroll-5.C testcase ICEs because cp_parser_pragma_unroll
during parsing calls maybe_constant_value unconditionally, which is
fine if !processing_template_decl, but can ICE otherwise.

While just calling fold_non_dependent_expr there instead could be enough
to fix the ICE (and I guess the right thing to do for backports if any),
I don't see a reason why we couldn't handle a dependent #pragma GCC unroll
argument as well, the unrolling isn't done in the FE and all the middle-end
cares about is that ANNOTATE_EXPR has a 1..65534 last operand when it is
annot_expr_unroll_kind.

So, the following patch changes all the unsigned short unroll arguments
to tree unroll (and thus avoids the tree -> unsigned short -> tree
conversions), does the type and value checking during parsing only if
the argument isn't dependent and repeats it during instantiation.

Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

2023-12-02  Jakub Jelinek  <jakub@redhat.com>

	PR c++/112795
gcc/cp/
	* cp-tree.h (cp_convert_range_for): Change UNROLL type from
	unsigned short to tree.
	(finish_while_stmt_cond, finish_do_stmt, finish_for_cond): Likewise.
	* parser.cc (cp_parser_statement): Pass NULL_TREE rather than 0 to
	cp_parser_iteration_statement UNROLL argument.
	(cp_parser_for, cp_parser_c_for): Change UNROLL type from
	unsigned short to tree.
	(cp_parser_range_for): Likewise.  Set RANGE_FOR_UNROLL to just UNROLL
	rather than build_int_cst from it.
	(cp_convert_range_for, cp_parser_iteration_statement): Change UNROLL
	type from unsigned short to tree.
	(cp_parser_omp_loop_nest): Pass NULL_TREE rather than 0 to
	cp_parser_range_for UNROLL argument.
	(cp_parser_pragma_unroll): Return tree rather than unsigned short.
	If parsed expression is type dependent, just return it, don't diagnose
	issues with value if it is value dependent.
	(cp_parser_pragma): Change UNROLL type from unsigned short to tree.
	* semantics.cc (finish_while_stmt_cond): Change UNROLL type from
	unsigned short to tree.  Build ANNOTATE_EXPR with UNROLL as its last
	operand rather than build_int_cst from it.
	(finish_do_stmt, finish_for_cond): Likewise.
	* pt.cc (tsubst_stmt) <case RANGE_FOR_STMT>: Change UNROLL type from
	unsigned short to tree and set it to RECUR on RANGE_FOR_UNROLL (t).
	(tsubst_expr) <case ANNOTATE_EXPR>: For annot_expr_unroll_kind repeat
	checks on UNROLL value from cp_parser_pragma_unroll.
gcc/testsuite/
	* g++.dg/ext/unroll-5.C: New test.
	* g++.dg/ext/unroll-6.C: New test.


	Jakub
  

Comments

Jason Merrill Dec. 4, 2023, 3:49 a.m. UTC | #1
On 12/2/23 05:51, Jakub Jelinek wrote:
> Hi!
> 
> foo in the unroll-5.C testcase ICEs because cp_parser_pragma_unroll
> during parsing calls maybe_constant_value unconditionally, which is
> fine if !processing_template_decl, but can ICE otherwise.
> 
> While just calling fold_non_dependent_expr there instead could be enough
> to fix the ICE (and I guess the right thing to do for backports if any),
> I don't see a reason why we couldn't handle a dependent #pragma GCC unroll
> argument as well, the unrolling isn't done in the FE and all the middle-end
> cares about is that ANNOTATE_EXPR has a 1..65534 last operand when it is
> annot_expr_unroll_kind.
> 
> So, the following patch changes all the unsigned short unroll arguments
> to tree unroll (and thus avoids the tree -> unsigned short -> tree
> conversions), does the type and value checking during parsing only if
> the argument isn't dependent and repeats it during instantiation.
> 
> Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

OK.

> 2023-12-02  Jakub Jelinek  <jakub@redhat.com>
> 
> 	PR c++/112795
> gcc/cp/
> 	* cp-tree.h (cp_convert_range_for): Change UNROLL type from
> 	unsigned short to tree.
> 	(finish_while_stmt_cond, finish_do_stmt, finish_for_cond): Likewise.
> 	* parser.cc (cp_parser_statement): Pass NULL_TREE rather than 0 to
> 	cp_parser_iteration_statement UNROLL argument.
> 	(cp_parser_for, cp_parser_c_for): Change UNROLL type from
> 	unsigned short to tree.
> 	(cp_parser_range_for): Likewise.  Set RANGE_FOR_UNROLL to just UNROLL
> 	rather than build_int_cst from it.
> 	(cp_convert_range_for, cp_parser_iteration_statement): Change UNROLL
> 	type from unsigned short to tree.
> 	(cp_parser_omp_loop_nest): Pass NULL_TREE rather than 0 to
> 	cp_parser_range_for UNROLL argument.
> 	(cp_parser_pragma_unroll): Return tree rather than unsigned short.
> 	If parsed expression is type dependent, just return it, don't diagnose
> 	issues with value if it is value dependent.
> 	(cp_parser_pragma): Change UNROLL type from unsigned short to tree.
> 	* semantics.cc (finish_while_stmt_cond): Change UNROLL type from
> 	unsigned short to tree.  Build ANNOTATE_EXPR with UNROLL as its last
> 	operand rather than build_int_cst from it.
> 	(finish_do_stmt, finish_for_cond): Likewise.
> 	* pt.cc (tsubst_stmt) <case RANGE_FOR_STMT>: Change UNROLL type from
> 	unsigned short to tree and set it to RECUR on RANGE_FOR_UNROLL (t).
> 	(tsubst_expr) <case ANNOTATE_EXPR>: For annot_expr_unroll_kind repeat
> 	checks on UNROLL value from cp_parser_pragma_unroll.
> gcc/testsuite/
> 	* g++.dg/ext/unroll-5.C: New test.
> 	* g++.dg/ext/unroll-6.C: New test.
> 
> --- gcc/cp/cp-tree.h.jj	2023-12-01 08:10:42.707324577 +0100
> +++ gcc/cp/cp-tree.h	2023-12-01 16:08:20.152165244 +0100
> @@ -7371,7 +7371,7 @@ extern bool maybe_clone_body			(tree);
>   
>   /* In parser.cc */
>   extern tree cp_convert_range_for (tree, tree, tree, cp_decomp *, bool,
> -				  unsigned short, bool);
> +				  tree, bool);
>   extern void cp_convert_omp_range_for (tree &, tree &, tree &,
>   				      tree &, tree &, tree &, tree &, tree &);
>   extern void cp_finish_omp_range_for (tree, tree);
> @@ -7692,19 +7692,16 @@ extern void begin_else_clause			(tree);
>   extern void finish_else_clause			(tree);
>   extern void finish_if_stmt			(tree);
>   extern tree begin_while_stmt			(void);
> -extern void finish_while_stmt_cond	(tree, tree, bool, unsigned short,
> -					 bool);
> +extern void finish_while_stmt_cond	(tree, tree, bool, tree, bool);
>   extern void finish_while_stmt			(tree);
>   extern tree begin_do_stmt			(void);
>   extern void finish_do_body			(tree);
> -extern void finish_do_stmt		(tree, tree, bool, unsigned short,
> -					 bool);
> +extern void finish_do_stmt		(tree, tree, bool, tree, bool);
>   extern tree finish_return_stmt			(tree);
>   extern tree begin_for_scope			(tree *);
>   extern tree begin_for_stmt			(tree, tree);
>   extern void finish_init_stmt			(tree);
> -extern void finish_for_cond		(tree, tree, bool, unsigned short,
> -					 bool);
> +extern void finish_for_cond		(tree, tree, bool, tree, bool);
>   extern void finish_for_expr			(tree, tree);
>   extern void finish_for_stmt			(tree);
>   extern tree begin_range_for_stmt		(tree, tree);
> --- gcc/cp/parser.cc.jj	2023-12-01 08:10:42.800323262 +0100
> +++ gcc/cp/parser.cc	2023-12-02 08:52:45.254387503 +0100
> @@ -2391,15 +2391,15 @@ static tree cp_parser_selection_statemen
>   static tree cp_parser_condition
>     (cp_parser *);
>   static tree cp_parser_iteration_statement
> -  (cp_parser *, bool *, bool, unsigned short, bool);
> +  (cp_parser *, bool *, bool, tree, bool);
>   static bool cp_parser_init_statement
>     (cp_parser *, tree *decl);
>   static tree cp_parser_for
> -  (cp_parser *, bool, unsigned short, bool);
> +  (cp_parser *, bool, tree, bool);
>   static tree cp_parser_c_for
> -  (cp_parser *, tree, tree, bool, unsigned short, bool);
> +  (cp_parser *, tree, tree, bool, tree, bool);
>   static tree cp_parser_range_for
> -  (cp_parser *, tree, tree, tree, bool, unsigned short, bool, bool);
> +  (cp_parser *, tree, tree, tree, bool, tree, bool, bool);
>   static void do_range_for_auto_deduction
>     (tree, tree, cp_decomp *);
>   static tree cp_parser_perform_range_for_lookup
> @@ -12521,8 +12521,8 @@ cp_parser_statement (cp_parser* parser,
>   	case RID_DO:
>   	case RID_FOR:
>   	  std_attrs = process_stmt_hotness_attribute (std_attrs, attrs_loc);
> -	  statement = cp_parser_iteration_statement (parser, if_p, false, 0,
> -						     false);
> +	  statement = cp_parser_iteration_statement (parser, if_p, false,
> +						     NULL_TREE, false);
>   	  break;
>   
>   	case RID_BREAK:
> @@ -13806,8 +13806,7 @@ cp_parser_condition (cp_parser* parser)
>      not included. */
>   
>   static tree
> -cp_parser_for (cp_parser *parser, bool ivdep, unsigned short unroll,
> -	       bool novector)
> +cp_parser_for (cp_parser *parser, bool ivdep, tree unroll, bool novector)
>   {
>     tree init, scope, decl;
>     bool is_range_for;
> @@ -13844,7 +13843,7 @@ cp_parser_for (cp_parser *parser, bool i
>   
>   static tree
>   cp_parser_c_for (cp_parser *parser, tree scope, tree init, bool ivdep,
> -		 unsigned short unroll, bool novector)
> +		 tree unroll, bool novector)
>   {
>     /* Normal for loop */
>     tree condition = NULL_TREE;
> @@ -13895,8 +13894,7 @@ cp_parser_c_for (cp_parser *parser, tree
>   
>   static tree
>   cp_parser_range_for (cp_parser *parser, tree scope, tree init, tree range_decl,
> -		     bool ivdep, unsigned short unroll, bool novector,
> -		     bool is_omp)
> +		     bool ivdep, tree unroll, bool novector, bool is_omp)
>   {
>     tree stmt, range_expr;
>     auto_vec <cxx_binding *, 16> bindings;
> @@ -13969,7 +13967,7 @@ cp_parser_range_for (cp_parser *parser,
>         if (ivdep)
>   	RANGE_FOR_IVDEP (stmt) = 1;
>         if (unroll)
> -	RANGE_FOR_UNROLL (stmt) = build_int_cst (integer_type_node, unroll);
> +	RANGE_FOR_UNROLL (stmt) = unroll;
>         if (novector)
>   	RANGE_FOR_NOVECTOR (stmt) = 1;
>         finish_range_for_decl (stmt, range_decl, range_expr);
> @@ -14158,7 +14156,7 @@ warn_for_range_copy (tree decl, tree exp
>   
>   tree
>   cp_convert_range_for (tree statement, tree range_decl, tree range_expr,
> -		      cp_decomp *decomp, bool ivdep, unsigned short unroll,
> +		      cp_decomp *decomp, bool ivdep, tree unroll,
>   		      bool novector)
>   {
>     tree begin, end;
> @@ -14383,7 +14381,7 @@ cp_parser_range_for_member_function (tre
>   
>   static tree
>   cp_parser_iteration_statement (cp_parser* parser, bool *if_p, bool ivdep,
> -			       unsigned short unroll, bool novector)
> +			       tree unroll, bool novector)
>   {
>     cp_token *token;
>     enum rid keyword;
> @@ -44303,7 +44301,7 @@ cp_parser_omp_loop_nest (cp_parser *pars
>   	  cp_parser_require (parser, CPP_COLON, RT_COLON);
>   
>   	  init = cp_parser_range_for (parser, NULL_TREE, NULL_TREE, decl,
> -				      false, 0, false, true);
> +				      false, NULL_TREE, false, true);
>   
>   	  cp_convert_omp_range_for (this_pre_body, sl, decl,
>   				    orig_decl, init, orig_init,
> @@ -50240,29 +50238,31 @@ cp_parser_pragma_ivdep (cp_parser *parse
>   
>   /* Parse a pragma GCC unroll.  */
>   
> -static unsigned short
> +static tree
>   cp_parser_pragma_unroll (cp_parser *parser, cp_token *pragma_tok)
>   {
>     location_t location = cp_lexer_peek_token (parser->lexer)->location;
> -  tree expr = cp_parser_constant_expression (parser);
> -  unsigned short unroll;
> -  expr = maybe_constant_value (expr);
> +  tree unroll = cp_parser_constant_expression (parser);
> +  unroll = fold_non_dependent_expr (unroll);
>     HOST_WIDE_INT lunroll = 0;
> -  if (!INTEGRAL_TYPE_P (TREE_TYPE (expr))
> -      || TREE_CODE (expr) != INTEGER_CST
> -      || (lunroll = tree_to_shwi (expr)) < 0
> -      || lunroll >= USHRT_MAX)
> +  if (type_dependent_expression_p (unroll))
> +    ;
> +  else if (!INTEGRAL_TYPE_P (TREE_TYPE (unroll))
> +	   || (!value_dependent_expression_p (unroll)
> +	       && (!tree_fits_shwi_p (unroll)
> +		   || (lunroll = tree_to_shwi (unroll)) < 0
> +		   || lunroll >= USHRT_MAX)))
>       {
>         error_at (location, "%<#pragma GCC unroll%> requires an"
>   		" assignment-expression that evaluates to a non-negative"
>   		" integral constant less than %u", USHRT_MAX);
> -      unroll = 0;
> +      unroll = NULL_TREE;
>       }
> -  else
> +  else if (TREE_CODE (unroll) == INTEGER_CST)
>       {
> -      unroll = (unsigned short)lunroll;
> -      if (unroll == 0)
> -	unroll = 1;
> +      unroll = fold_convert (integer_type_node, unroll);
> +      if (integer_zerop (unroll))
> +	unroll = integer_one_node;
>       }
>     cp_parser_skip_to_pragma_eol (parser, pragma_tok);
>     return unroll;
> @@ -50597,7 +50597,7 @@ cp_parser_pragma (cp_parser *parser, enu
>       case PRAGMA_NOVECTOR:
>         {
>   	bool ivdep;
> -	unsigned short unroll = 0;
> +	tree unroll = NULL_TREE;
>   	bool novector = false;
>   	const char *pragma_str;
>   
> --- gcc/cp/semantics.cc.jj	2023-12-01 08:10:42.826322894 +0100
> +++ gcc/cp/semantics.cc	2023-12-01 15:55:58.998529436 +0100
> @@ -1166,7 +1166,7 @@ begin_while_stmt (void)
>   
>   void
>   finish_while_stmt_cond (tree cond, tree while_stmt, bool ivdep,
> -			unsigned short unroll, bool novector)
> +			tree unroll, bool novector)
>   {
>     cond = maybe_convert_cond (cond);
>     finish_cond (&WHILE_COND (while_stmt), cond);
> @@ -1184,8 +1184,7 @@ finish_while_stmt_cond (tree cond, tree
>   				      WHILE_COND (while_stmt),
>   				      build_int_cst (integer_type_node,
>   						     annot_expr_unroll_kind),
> -				      build_int_cst (integer_type_node,
> -						     unroll));
> +				      unroll);
>     if (novector && cond != error_mark_node)
>       WHILE_COND (while_stmt) = build3 (ANNOTATE_EXPR,
>   				      TREE_TYPE (WHILE_COND (while_stmt)),
> @@ -1237,7 +1236,7 @@ finish_do_body (tree do_stmt)
>      COND is as indicated.  */
>   
>   void
> -finish_do_stmt (tree cond, tree do_stmt, bool ivdep, unsigned short unroll,
> +finish_do_stmt (tree cond, tree do_stmt, bool ivdep, tree unroll,
>   		bool novector)
>   {
>     cond = maybe_convert_cond (cond);
> @@ -1254,7 +1253,7 @@ finish_do_stmt (tree cond, tree do_stmt,
>     if (unroll && cond != error_mark_node)
>       cond = build3 (ANNOTATE_EXPR, TREE_TYPE (cond), cond,
>   		   build_int_cst (integer_type_node, annot_expr_unroll_kind),
> -		   build_int_cst (integer_type_node, unroll));
> +		   unroll);
>     if (novector && cond != error_mark_node)
>       cond = build3 (ANNOTATE_EXPR, TREE_TYPE (cond), cond,
>   		   build_int_cst (integer_type_node, annot_expr_no_vector_kind),
> @@ -1357,7 +1356,8 @@ finish_init_stmt (tree for_stmt)
>      FOR_STMT.  */
>   
>   void
> -finish_for_cond (tree cond, tree for_stmt, bool ivdep, unsigned short unroll, bool novector)
> +finish_for_cond (tree cond, tree for_stmt, bool ivdep, tree unroll,
> +		 bool novector)
>   {
>     cond = maybe_convert_cond (cond);
>     finish_cond (&FOR_COND (for_stmt), cond);
> @@ -1375,8 +1375,7 @@ finish_for_cond (tree cond, tree for_stm
>   				  FOR_COND (for_stmt),
>   				  build_int_cst (integer_type_node,
>   						 annot_expr_unroll_kind),
> -				  build_int_cst (integer_type_node,
> -						 unroll));
> +				  unroll);
>     if (novector && cond != error_mark_node)
>       FOR_COND (for_stmt) = build3 (ANNOTATE_EXPR,
>   				  TREE_TYPE (FOR_COND (for_stmt)),
> --- gcc/cp/pt.cc.jj	2023-12-01 08:10:42.819322994 +0100
> +++ gcc/cp/pt.cc	2023-12-01 16:43:26.589676880 +0100
> @@ -18418,8 +18418,7 @@ tsubst_stmt (tree t, tree args, tsubst_f
>   	  }
>   	else
>   	  {
> -	    unsigned short unroll = (RANGE_FOR_UNROLL (t)
> -				     ? tree_to_uhwi (RANGE_FOR_UNROLL (t)) : 0);
> +	    tree unroll = RECUR (RANGE_FOR_UNROLL (t));
>   	    stmt = cp_convert_range_for (stmt, decl, expr, decomp,
>   					 RANGE_FOR_IVDEP (t), unroll,
>   					 RANGE_FOR_NOVECTOR (t));
> @@ -21501,11 +21500,39 @@ tsubst_expr (tree t, tree args, tsubst_f
>         }
>   
>       case ANNOTATE_EXPR:
> -      op1 = RECUR (TREE_OPERAND (t, 0));
> -      RETURN (build3_loc (EXPR_LOCATION (t), ANNOTATE_EXPR,
> -			  TREE_TYPE (op1), op1,
> -			  RECUR (TREE_OPERAND (t, 1)),
> -			  RECUR (TREE_OPERAND (t, 2))));
> +      {
> +	op1 = RECUR (TREE_OPERAND (t, 0));
> +	tree op2 = RECUR (TREE_OPERAND (t, 1));
> +	tree op3 = RECUR (TREE_OPERAND (t, 2));
> +	if (TREE_CODE (op2) == INTEGER_CST
> +	    && wi::to_widest (op2) == (int) annot_expr_unroll_kind)
> +	  {
> +	    HOST_WIDE_INT lunroll;
> +	    if (type_dependent_expression_p (op3))
> +	      ;
> +	    else if (!INTEGRAL_TYPE_P (TREE_TYPE (op3))
> +		     || (!value_dependent_expression_p (op3)
> +			 && (!tree_fits_shwi_p (op3)
> +			     || (lunroll = tree_to_shwi (op3)) < 0
> +			     || lunroll >= USHRT_MAX)))
> +	      {
> +		error_at (EXPR_LOCATION (TREE_OPERAND (t, 2)),
> +			  "%<#pragma GCC unroll%> requires an "
> +			  "assignment-expression that evaluates to a "
> +			  "non-negative integral constant less than %u",
> +			  USHRT_MAX);
> +		op3 = integer_one_node;
> +	      }
> +	    else if (TREE_CODE (op3) == INTEGER_CST)
> +	      {
> +		op3 = fold_convert (integer_type_node, op3);
> +		if (integer_zerop (op3))
> +		  op3 = integer_one_node;
> +	      }
> +	  }
> +	RETURN (build3_loc (EXPR_LOCATION (t), ANNOTATE_EXPR,
> +			    TREE_TYPE (op1), op1, op2, op3));
> +      }
>   
>       default:
>         /* Handle Objective-C++ constructs, if appropriate.  */
> --- gcc/testsuite/g++.dg/ext/unroll-5.C.jj	2023-12-01 16:26:10.507186363 +0100
> +++ gcc/testsuite/g++.dg/ext/unroll-5.C	2023-12-01 16:32:52.341560326 +0100
> @@ -0,0 +1,36 @@
> +// PR c++/112795
> +// { dg-do compile { target c++11 } }
> +// { dg-options "-O2 -fdump-tree-cunrolli-details" }
> +
> +void baz (int);
> +constexpr int n = 3;
> +
> +template <int N>
> +void
> +foo ()
> +{
> +#pragma GCC unroll(n)
> +  for (int i = 0; i != n; ++i)
> +    baz (i);
> +}
> +
> +template <int N>
> +void
> +bar ()
> +{
> +#pragma GCC unroll(N)
> +  for (int i = 0; i != N; ++i)
> +    baz (i);
> +}
> +
> +void
> +qux ()
> +{
> +  foo <2> ();
> +  bar <6> ();
> +  bar <10> ();
> +}
> +
> +// { dg-final { scan-tree-dump "loop with 3 iterations completely unrolled" "cunrolli" } }
> +// { dg-final { scan-tree-dump "loop with 6 iterations completely unrolled" "cunrolli" } }
> +// { dg-final { scan-tree-dump "loop with 10 iterations completely unrolled" "cunrolli" } }
> --- gcc/testsuite/g++.dg/ext/unroll-6.C.jj	2023-12-01 16:28:13.738461284 +0100
> +++ gcc/testsuite/g++.dg/ext/unroll-6.C	2023-12-01 16:45:40.599799896 +0100
> @@ -0,0 +1,85 @@
> +// PR c++/112795
> +// { dg-do compile { target c++11 } }
> +
> +void
> +foo ()
> +{
> +  #pragma GCC unroll 1.0f			// { dg-error "'#pragma GCC unroll' requires an assignment-expression that evaluates to a non-negative integral constant less than" }
> +  for (int i = 0; i < 2; ++i)
> +    ;
> +  #pragma GCC unroll 0xffffffffffffffffULL	// { dg-error "'#pragma GCC unroll' requires an assignment-expression that evaluates to a non-negative integral constant less than" }
> +  for (int i = 0; i < 2; ++i)
> +    ;
> +  #pragma GCC unroll -42			// { dg-error "'#pragma GCC unroll' requires an assignment-expression that evaluates to a non-negative integral constant less than" }
> +  for (int i = 0; i < 2; ++i)
> +    ;
> +}
> +
> +template <int N>
> +void
> +bar ()
> +{
> +  #pragma GCC unroll 1.0f			// { dg-error "'#pragma GCC unroll' requires an assignment-expression that evaluates to a non-negative integral constant less than" }
> +  for (int i = 0; i < 2; ++i)
> +    ;
> +  #pragma GCC unroll 0xffffffffffffffffULL	// { dg-error "'#pragma GCC unroll' requires an assignment-expression that evaluates to a non-negative integral constant less than" }
> +  for (int i = 0; i < 2; ++i)
> +    ;
> +  #pragma GCC unroll -42			// { dg-error "'#pragma GCC unroll' requires an assignment-expression that evaluates to a non-negative integral constant less than" }
> +  for (int i = 0; i < 2; ++i)
> +    ;
> +}
> +
> +template <typename T, int N>
> +void
> +baz ()
> +{
> +  #pragma GCC unroll (N + 1.0f)			// { dg-error "'#pragma GCC unroll' requires an assignment-expression that evaluates to a non-negative integral constant less than" }
> +  for (int i = 0; i < 2; ++i)
> +    ;
> +  #pragma GCC unroll (N + 0xffffffffffffffffULL)
> +  for (int i = 0; i < 2; ++i)
> +    ;
> +  #pragma GCC unroll (N - 42)
> +  for (int i = 0; i < 2; ++i)
> +    ;
> +  #pragma GCC unroll ((T) 1.0f)
> +  for (int i = 0; i < 2; ++i)
> +    ;
> +  #pragma GCC unroll ((T) 0xffffffffffffffffULL)
> +  for (int i = 0; i < 2; ++i)
> +    ;
> +  #pragma GCC unroll ((T) -42)
> +  for (int i = 0; i < 2; ++i)
> +    ;
> +}
> +
> +template <typename T, int N>
> +void
> +qux ()
> +{
> +  #pragma GCC unroll (N + 1.0f)			// { dg-error "'#pragma GCC unroll' requires an assignment-expression that evaluates to a non-negative integral constant less than" }
> +  for (int i = 0; i < 2; ++i)
> +    ;
> +  #pragma GCC unroll (N + 0xffffffffffffffffULL)// { dg-error "'#pragma GCC unroll' requires an assignment-expression that evaluates to a non-negative integral constant less than" }
> +  for (int i = 0; i < 2; ++i)
> +    ;
> +  #pragma GCC unroll (N - 42)			// { dg-error "'#pragma GCC unroll' requires an assignment-expression that evaluates to a non-negative integral constant less than" }
> +  for (int i = 0; i < 2; ++i)
> +    ;
> +  #pragma GCC unroll ((T) 1.0f)			// { dg-error "'#pragma GCC unroll' requires an assignment-expression that evaluates to a non-negative integral constant less than" }
> +  for (int i = 0; i < 2; ++i)
> +    ;
> +  #pragma GCC unroll ((T) 0xffffffffffffffffULL)// { dg-error "'#pragma GCC unroll' requires an assignment-expression that evaluates to a non-negative integral constant less than" }
> +  for (int i = 0; i < 2; ++i)
> +    ;
> +  #pragma GCC unroll ((T) -42)			// { dg-error "'#pragma GCC unroll' requires an assignment-expression that evaluates to a non-negative integral constant less than" }
> +  for (int i = 0; i < 2; ++i)
> +    ;
> +}
> +
> +void
> +corge ()
> +{
> +  qux <float, 0> ();
> +}
> 
> 	Jakub
>
  

Patch

--- gcc/cp/cp-tree.h.jj	2023-12-01 08:10:42.707324577 +0100
+++ gcc/cp/cp-tree.h	2023-12-01 16:08:20.152165244 +0100
@@ -7371,7 +7371,7 @@  extern bool maybe_clone_body			(tree);
 
 /* In parser.cc */
 extern tree cp_convert_range_for (tree, tree, tree, cp_decomp *, bool,
-				  unsigned short, bool);
+				  tree, bool);
 extern void cp_convert_omp_range_for (tree &, tree &, tree &,
 				      tree &, tree &, tree &, tree &, tree &);
 extern void cp_finish_omp_range_for (tree, tree);
@@ -7692,19 +7692,16 @@  extern void begin_else_clause			(tree);
 extern void finish_else_clause			(tree);
 extern void finish_if_stmt			(tree);
 extern tree begin_while_stmt			(void);
-extern void finish_while_stmt_cond	(tree, tree, bool, unsigned short,
-					 bool);
+extern void finish_while_stmt_cond	(tree, tree, bool, tree, bool);
 extern void finish_while_stmt			(tree);
 extern tree begin_do_stmt			(void);
 extern void finish_do_body			(tree);
-extern void finish_do_stmt		(tree, tree, bool, unsigned short,
-					 bool);
+extern void finish_do_stmt		(tree, tree, bool, tree, bool);
 extern tree finish_return_stmt			(tree);
 extern tree begin_for_scope			(tree *);
 extern tree begin_for_stmt			(tree, tree);
 extern void finish_init_stmt			(tree);
-extern void finish_for_cond		(tree, tree, bool, unsigned short,
-					 bool);
+extern void finish_for_cond		(tree, tree, bool, tree, bool);
 extern void finish_for_expr			(tree, tree);
 extern void finish_for_stmt			(tree);
 extern tree begin_range_for_stmt		(tree, tree);
--- gcc/cp/parser.cc.jj	2023-12-01 08:10:42.800323262 +0100
+++ gcc/cp/parser.cc	2023-12-02 08:52:45.254387503 +0100
@@ -2391,15 +2391,15 @@  static tree cp_parser_selection_statemen
 static tree cp_parser_condition
   (cp_parser *);
 static tree cp_parser_iteration_statement
-  (cp_parser *, bool *, bool, unsigned short, bool);
+  (cp_parser *, bool *, bool, tree, bool);
 static bool cp_parser_init_statement
   (cp_parser *, tree *decl);
 static tree cp_parser_for
-  (cp_parser *, bool, unsigned short, bool);
+  (cp_parser *, bool, tree, bool);
 static tree cp_parser_c_for
-  (cp_parser *, tree, tree, bool, unsigned short, bool);
+  (cp_parser *, tree, tree, bool, tree, bool);
 static tree cp_parser_range_for
-  (cp_parser *, tree, tree, tree, bool, unsigned short, bool, bool);
+  (cp_parser *, tree, tree, tree, bool, tree, bool, bool);
 static void do_range_for_auto_deduction
   (tree, tree, cp_decomp *);
 static tree cp_parser_perform_range_for_lookup
@@ -12521,8 +12521,8 @@  cp_parser_statement (cp_parser* parser,
 	case RID_DO:
 	case RID_FOR:
 	  std_attrs = process_stmt_hotness_attribute (std_attrs, attrs_loc);
-	  statement = cp_parser_iteration_statement (parser, if_p, false, 0,
-						     false);
+	  statement = cp_parser_iteration_statement (parser, if_p, false,
+						     NULL_TREE, false);
 	  break;
 
 	case RID_BREAK:
@@ -13806,8 +13806,7 @@  cp_parser_condition (cp_parser* parser)
    not included. */
 
 static tree
-cp_parser_for (cp_parser *parser, bool ivdep, unsigned short unroll,
-	       bool novector)
+cp_parser_for (cp_parser *parser, bool ivdep, tree unroll, bool novector)
 {
   tree init, scope, decl;
   bool is_range_for;
@@ -13844,7 +13843,7 @@  cp_parser_for (cp_parser *parser, bool i
 
 static tree
 cp_parser_c_for (cp_parser *parser, tree scope, tree init, bool ivdep,
-		 unsigned short unroll, bool novector)
+		 tree unroll, bool novector)
 {
   /* Normal for loop */
   tree condition = NULL_TREE;
@@ -13895,8 +13894,7 @@  cp_parser_c_for (cp_parser *parser, tree
 
 static tree
 cp_parser_range_for (cp_parser *parser, tree scope, tree init, tree range_decl,
-		     bool ivdep, unsigned short unroll, bool novector,
-		     bool is_omp)
+		     bool ivdep, tree unroll, bool novector, bool is_omp)
 {
   tree stmt, range_expr;
   auto_vec <cxx_binding *, 16> bindings;
@@ -13969,7 +13967,7 @@  cp_parser_range_for (cp_parser *parser,
       if (ivdep)
 	RANGE_FOR_IVDEP (stmt) = 1;
       if (unroll)
-	RANGE_FOR_UNROLL (stmt) = build_int_cst (integer_type_node, unroll);
+	RANGE_FOR_UNROLL (stmt) = unroll;
       if (novector)
 	RANGE_FOR_NOVECTOR (stmt) = 1;
       finish_range_for_decl (stmt, range_decl, range_expr);
@@ -14158,7 +14156,7 @@  warn_for_range_copy (tree decl, tree exp
 
 tree
 cp_convert_range_for (tree statement, tree range_decl, tree range_expr,
-		      cp_decomp *decomp, bool ivdep, unsigned short unroll,
+		      cp_decomp *decomp, bool ivdep, tree unroll,
 		      bool novector)
 {
   tree begin, end;
@@ -14383,7 +14381,7 @@  cp_parser_range_for_member_function (tre
 
 static tree
 cp_parser_iteration_statement (cp_parser* parser, bool *if_p, bool ivdep,
-			       unsigned short unroll, bool novector)
+			       tree unroll, bool novector)
 {
   cp_token *token;
   enum rid keyword;
@@ -44303,7 +44301,7 @@  cp_parser_omp_loop_nest (cp_parser *pars
 	  cp_parser_require (parser, CPP_COLON, RT_COLON);
 
 	  init = cp_parser_range_for (parser, NULL_TREE, NULL_TREE, decl,
-				      false, 0, false, true);
+				      false, NULL_TREE, false, true);
 
 	  cp_convert_omp_range_for (this_pre_body, sl, decl,
 				    orig_decl, init, orig_init,
@@ -50240,29 +50238,31 @@  cp_parser_pragma_ivdep (cp_parser *parse
 
 /* Parse a pragma GCC unroll.  */
 
-static unsigned short
+static tree
 cp_parser_pragma_unroll (cp_parser *parser, cp_token *pragma_tok)
 {
   location_t location = cp_lexer_peek_token (parser->lexer)->location;
-  tree expr = cp_parser_constant_expression (parser);
-  unsigned short unroll;
-  expr = maybe_constant_value (expr);
+  tree unroll = cp_parser_constant_expression (parser);
+  unroll = fold_non_dependent_expr (unroll);
   HOST_WIDE_INT lunroll = 0;
-  if (!INTEGRAL_TYPE_P (TREE_TYPE (expr))
-      || TREE_CODE (expr) != INTEGER_CST
-      || (lunroll = tree_to_shwi (expr)) < 0
-      || lunroll >= USHRT_MAX)
+  if (type_dependent_expression_p (unroll))
+    ;
+  else if (!INTEGRAL_TYPE_P (TREE_TYPE (unroll))
+	   || (!value_dependent_expression_p (unroll)
+	       && (!tree_fits_shwi_p (unroll)
+		   || (lunroll = tree_to_shwi (unroll)) < 0
+		   || lunroll >= USHRT_MAX)))
     {
       error_at (location, "%<#pragma GCC unroll%> requires an"
 		" assignment-expression that evaluates to a non-negative"
 		" integral constant less than %u", USHRT_MAX);
-      unroll = 0;
+      unroll = NULL_TREE;
     }
-  else
+  else if (TREE_CODE (unroll) == INTEGER_CST)
     {
-      unroll = (unsigned short)lunroll;
-      if (unroll == 0)
-	unroll = 1;
+      unroll = fold_convert (integer_type_node, unroll);
+      if (integer_zerop (unroll))
+	unroll = integer_one_node;
     }
   cp_parser_skip_to_pragma_eol (parser, pragma_tok);
   return unroll;
@@ -50597,7 +50597,7 @@  cp_parser_pragma (cp_parser *parser, enu
     case PRAGMA_NOVECTOR:
       {
 	bool ivdep;
-	unsigned short unroll = 0;
+	tree unroll = NULL_TREE;
 	bool novector = false;
 	const char *pragma_str;
 
--- gcc/cp/semantics.cc.jj	2023-12-01 08:10:42.826322894 +0100
+++ gcc/cp/semantics.cc	2023-12-01 15:55:58.998529436 +0100
@@ -1166,7 +1166,7 @@  begin_while_stmt (void)
 
 void
 finish_while_stmt_cond (tree cond, tree while_stmt, bool ivdep,
-			unsigned short unroll, bool novector)
+			tree unroll, bool novector)
 {
   cond = maybe_convert_cond (cond);
   finish_cond (&WHILE_COND (while_stmt), cond);
@@ -1184,8 +1184,7 @@  finish_while_stmt_cond (tree cond, tree
 				      WHILE_COND (while_stmt),
 				      build_int_cst (integer_type_node,
 						     annot_expr_unroll_kind),
-				      build_int_cst (integer_type_node,
-						     unroll));
+				      unroll);
   if (novector && cond != error_mark_node)
     WHILE_COND (while_stmt) = build3 (ANNOTATE_EXPR,
 				      TREE_TYPE (WHILE_COND (while_stmt)),
@@ -1237,7 +1236,7 @@  finish_do_body (tree do_stmt)
    COND is as indicated.  */
 
 void
-finish_do_stmt (tree cond, tree do_stmt, bool ivdep, unsigned short unroll,
+finish_do_stmt (tree cond, tree do_stmt, bool ivdep, tree unroll,
 		bool novector)
 {
   cond = maybe_convert_cond (cond);
@@ -1254,7 +1253,7 @@  finish_do_stmt (tree cond, tree do_stmt,
   if (unroll && cond != error_mark_node)
     cond = build3 (ANNOTATE_EXPR, TREE_TYPE (cond), cond,
 		   build_int_cst (integer_type_node, annot_expr_unroll_kind),
-		   build_int_cst (integer_type_node, unroll));
+		   unroll);
   if (novector && cond != error_mark_node)
     cond = build3 (ANNOTATE_EXPR, TREE_TYPE (cond), cond,
 		   build_int_cst (integer_type_node, annot_expr_no_vector_kind),
@@ -1357,7 +1356,8 @@  finish_init_stmt (tree for_stmt)
    FOR_STMT.  */
 
 void
-finish_for_cond (tree cond, tree for_stmt, bool ivdep, unsigned short unroll, bool novector)
+finish_for_cond (tree cond, tree for_stmt, bool ivdep, tree unroll,
+		 bool novector)
 {
   cond = maybe_convert_cond (cond);
   finish_cond (&FOR_COND (for_stmt), cond);
@@ -1375,8 +1375,7 @@  finish_for_cond (tree cond, tree for_stm
 				  FOR_COND (for_stmt),
 				  build_int_cst (integer_type_node,
 						 annot_expr_unroll_kind),
-				  build_int_cst (integer_type_node,
-						 unroll));
+				  unroll);
   if (novector && cond != error_mark_node)
     FOR_COND (for_stmt) = build3 (ANNOTATE_EXPR,
 				  TREE_TYPE (FOR_COND (for_stmt)),
--- gcc/cp/pt.cc.jj	2023-12-01 08:10:42.819322994 +0100
+++ gcc/cp/pt.cc	2023-12-01 16:43:26.589676880 +0100
@@ -18418,8 +18418,7 @@  tsubst_stmt (tree t, tree args, tsubst_f
 	  }
 	else
 	  {
-	    unsigned short unroll = (RANGE_FOR_UNROLL (t)
-				     ? tree_to_uhwi (RANGE_FOR_UNROLL (t)) : 0);
+	    tree unroll = RECUR (RANGE_FOR_UNROLL (t));
 	    stmt = cp_convert_range_for (stmt, decl, expr, decomp,
 					 RANGE_FOR_IVDEP (t), unroll,
 					 RANGE_FOR_NOVECTOR (t));
@@ -21501,11 +21500,39 @@  tsubst_expr (tree t, tree args, tsubst_f
       }
 
     case ANNOTATE_EXPR:
-      op1 = RECUR (TREE_OPERAND (t, 0));
-      RETURN (build3_loc (EXPR_LOCATION (t), ANNOTATE_EXPR,
-			  TREE_TYPE (op1), op1,
-			  RECUR (TREE_OPERAND (t, 1)),
-			  RECUR (TREE_OPERAND (t, 2))));
+      {
+	op1 = RECUR (TREE_OPERAND (t, 0));
+	tree op2 = RECUR (TREE_OPERAND (t, 1));
+	tree op3 = RECUR (TREE_OPERAND (t, 2));
+	if (TREE_CODE (op2) == INTEGER_CST
+	    && wi::to_widest (op2) == (int) annot_expr_unroll_kind)
+	  {
+	    HOST_WIDE_INT lunroll;
+	    if (type_dependent_expression_p (op3))
+	      ;
+	    else if (!INTEGRAL_TYPE_P (TREE_TYPE (op3))
+		     || (!value_dependent_expression_p (op3)
+			 && (!tree_fits_shwi_p (op3)
+			     || (lunroll = tree_to_shwi (op3)) < 0
+			     || lunroll >= USHRT_MAX)))
+	      {
+		error_at (EXPR_LOCATION (TREE_OPERAND (t, 2)),
+			  "%<#pragma GCC unroll%> requires an "
+			  "assignment-expression that evaluates to a "
+			  "non-negative integral constant less than %u",
+			  USHRT_MAX);
+		op3 = integer_one_node;
+	      }
+	    else if (TREE_CODE (op3) == INTEGER_CST)
+	      {
+		op3 = fold_convert (integer_type_node, op3);
+		if (integer_zerop (op3))
+		  op3 = integer_one_node;
+	      }
+	  }
+	RETURN (build3_loc (EXPR_LOCATION (t), ANNOTATE_EXPR,
+			    TREE_TYPE (op1), op1, op2, op3));
+      }
 
     default:
       /* Handle Objective-C++ constructs, if appropriate.  */
--- gcc/testsuite/g++.dg/ext/unroll-5.C.jj	2023-12-01 16:26:10.507186363 +0100
+++ gcc/testsuite/g++.dg/ext/unroll-5.C	2023-12-01 16:32:52.341560326 +0100
@@ -0,0 +1,36 @@ 
+// PR c++/112795
+// { dg-do compile { target c++11 } }
+// { dg-options "-O2 -fdump-tree-cunrolli-details" }
+
+void baz (int);
+constexpr int n = 3;
+
+template <int N>
+void
+foo ()
+{
+#pragma GCC unroll(n)
+  for (int i = 0; i != n; ++i)
+    baz (i);
+}
+
+template <int N>
+void
+bar ()
+{
+#pragma GCC unroll(N)
+  for (int i = 0; i != N; ++i)
+    baz (i);
+}
+
+void
+qux ()
+{
+  foo <2> ();
+  bar <6> ();
+  bar <10> ();
+}
+
+// { dg-final { scan-tree-dump "loop with 3 iterations completely unrolled" "cunrolli" } }
+// { dg-final { scan-tree-dump "loop with 6 iterations completely unrolled" "cunrolli" } }
+// { dg-final { scan-tree-dump "loop with 10 iterations completely unrolled" "cunrolli" } }
--- gcc/testsuite/g++.dg/ext/unroll-6.C.jj	2023-12-01 16:28:13.738461284 +0100
+++ gcc/testsuite/g++.dg/ext/unroll-6.C	2023-12-01 16:45:40.599799896 +0100
@@ -0,0 +1,85 @@ 
+// PR c++/112795
+// { dg-do compile { target c++11 } }
+
+void
+foo ()
+{
+  #pragma GCC unroll 1.0f			// { dg-error "'#pragma GCC unroll' requires an assignment-expression that evaluates to a non-negative integral constant less than" }
+  for (int i = 0; i < 2; ++i)
+    ;
+  #pragma GCC unroll 0xffffffffffffffffULL	// { dg-error "'#pragma GCC unroll' requires an assignment-expression that evaluates to a non-negative integral constant less than" }
+  for (int i = 0; i < 2; ++i)
+    ;
+  #pragma GCC unroll -42			// { dg-error "'#pragma GCC unroll' requires an assignment-expression that evaluates to a non-negative integral constant less than" }
+  for (int i = 0; i < 2; ++i)
+    ;
+}
+
+template <int N>
+void
+bar ()
+{
+  #pragma GCC unroll 1.0f			// { dg-error "'#pragma GCC unroll' requires an assignment-expression that evaluates to a non-negative integral constant less than" }
+  for (int i = 0; i < 2; ++i)
+    ;
+  #pragma GCC unroll 0xffffffffffffffffULL	// { dg-error "'#pragma GCC unroll' requires an assignment-expression that evaluates to a non-negative integral constant less than" }
+  for (int i = 0; i < 2; ++i)
+    ;
+  #pragma GCC unroll -42			// { dg-error "'#pragma GCC unroll' requires an assignment-expression that evaluates to a non-negative integral constant less than" }
+  for (int i = 0; i < 2; ++i)
+    ;
+}
+
+template <typename T, int N>
+void
+baz ()
+{
+  #pragma GCC unroll (N + 1.0f)			// { dg-error "'#pragma GCC unroll' requires an assignment-expression that evaluates to a non-negative integral constant less than" }
+  for (int i = 0; i < 2; ++i)
+    ;
+  #pragma GCC unroll (N + 0xffffffffffffffffULL)
+  for (int i = 0; i < 2; ++i)
+    ;
+  #pragma GCC unroll (N - 42)
+  for (int i = 0; i < 2; ++i)
+    ;
+  #pragma GCC unroll ((T) 1.0f)
+  for (int i = 0; i < 2; ++i)
+    ;
+  #pragma GCC unroll ((T) 0xffffffffffffffffULL)
+  for (int i = 0; i < 2; ++i)
+    ;
+  #pragma GCC unroll ((T) -42)
+  for (int i = 0; i < 2; ++i)
+    ;
+}
+
+template <typename T, int N>
+void
+qux ()
+{
+  #pragma GCC unroll (N + 1.0f)			// { dg-error "'#pragma GCC unroll' requires an assignment-expression that evaluates to a non-negative integral constant less than" }
+  for (int i = 0; i < 2; ++i)
+    ;
+  #pragma GCC unroll (N + 0xffffffffffffffffULL)// { dg-error "'#pragma GCC unroll' requires an assignment-expression that evaluates to a non-negative integral constant less than" }
+  for (int i = 0; i < 2; ++i)
+    ;
+  #pragma GCC unroll (N - 42)			// { dg-error "'#pragma GCC unroll' requires an assignment-expression that evaluates to a non-negative integral constant less than" }
+  for (int i = 0; i < 2; ++i)
+    ;
+  #pragma GCC unroll ((T) 1.0f)			// { dg-error "'#pragma GCC unroll' requires an assignment-expression that evaluates to a non-negative integral constant less than" }
+  for (int i = 0; i < 2; ++i)
+    ;
+  #pragma GCC unroll ((T) 0xffffffffffffffffULL)// { dg-error "'#pragma GCC unroll' requires an assignment-expression that evaluates to a non-negative integral constant less than" }
+  for (int i = 0; i < 2; ++i)
+    ;
+  #pragma GCC unroll ((T) -42)			// { dg-error "'#pragma GCC unroll' requires an assignment-expression that evaluates to a non-negative integral constant less than" }
+  for (int i = 0; i < 2; ++i)
+    ;
+}
+
+void
+corge ()
+{
+  qux <float, 0> ();
+}